From 7dc53f1e94cba0b2b9aa846108fd794abd3e15a4 Mon Sep 17 00:00:00 2001 From: will1742 Date: Wed, 9 Jun 2021 12:51:27 -0400 Subject: [PATCH] Initial Master Commit --- Haas_Gantry_BIDC/haas gantry BIDC.cps | 3278 +++++++++++++++ Haas_Next_Generation/haas next generation.cps | 3545 ++++++++++++++++ Haas_ST-20Y/haas st-20y.cps | 3738 +++++++++++++++++ Haas_Turning/haas turning.cps | 1727 ++++++++ README.md | 4 +- 5 files changed, 12291 insertions(+), 1 deletion(-) create mode 100644 Haas_Gantry_BIDC/haas gantry BIDC.cps create mode 100644 Haas_Next_Generation/haas next generation.cps create mode 100644 Haas_ST-20Y/haas st-20y.cps create mode 100644 Haas_Turning/haas turning.cps diff --git a/Haas_Gantry_BIDC/haas gantry BIDC.cps b/Haas_Gantry_BIDC/haas gantry BIDC.cps new file mode 100644 index 0000000..621ffad --- /dev/null +++ b/Haas_Gantry_BIDC/haas gantry BIDC.cps @@ -0,0 +1,3278 @@ +/** + Copyright (C) 2012-2020 by Autodesk, Inc. + All rights reserved. + + HAAS post processor configuration. + + $Revision: 42948 18db16d6a91dc1b324e18367ed37053e7bcec12d $ + $Date: 2020-09-25 07:13:44 $ + + FORKID {241E0993-8BE0-463b-8888-47968B9D7F9F} +*/ + +description = "HAAS Gantry BIDC"; +vendor = "Haas Automation"; +vendorUrl = "https://www.haascnc.com"; +legal = "Copyright (C) 2012-2020 by Autodesk, Inc."; +certificationLevel = 2; +minimumRevision = 40783; + +longDescription = "Generic post for use with all common 3-axis HAAS mills like the DM, VF, Office Mill, and Mini Mill series. This post is for the pre-Next Generation Control. By default positioning moves will be output as high feed G1s instead of G0s. You can turn on the property 'useG0' to force G0s but be careful as the CNC will follow a dogleg path rather than a direct path."; + +extension = "nc"; +programNameIsInteger = true; +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(355); +allowHelicalMoves = true; +allowedCircularPlanes = undefined; // allow any circular motion +allowSpiralMoves = true; +highFeedrate = (unit == IN) ? 500 : 5000; + +// user-defined properties +properties = { + writeMachine: true, // write machine + writeTools: true, // writes the tools + writeVersion: false, // include version info + preloadTool: true, // preloads next tool on tool change if any + chipTransport: false, // turn on chip transport at start of program + showSequenceNumbers: true, // show sequence numbers + sequenceNumberStart: 10, // first sequence number + sequenceNumberIncrement: 5, // increment for sequence numbers + sequenceNumberOnlyOnToolChange: false, // only output sequence numbers on tool change + optionalStop: true, // optional stop + separateWordsWithSpace: true, // specifies that the words should be separated with a white space + useRadius: false, // specifies that arcs should be output using the radius (R word) instead of the I, J, and K words. + useParametricFeed: false, // specifies that feed should be output using Q values + showNotes: false, // specifies that operation notes should be output + useG0: false, // allow G0 when moving along more than one axis + useG28: false, // specifies that G28 should be used instead of G53 + useSubroutines: false, // specifies that subroutines should be generated + useSubroutinePatterns: false, // generates subroutines for patterned operation + useSubroutineCycles: false, // generates subroutines for cycle operations on same holes + useG187: false, // use G187 to set smoothing on the machine + homePositionCenter: true, // moves the part in X in center of the door at end of program (ONLY WORKS IF THE TABLE IS MOVING) + optionallyCycleToolsAtStart: false, // cycle through each tool used at the beginning of the program when block delete is turned off - this allows the operator to easily measure all tools before they are used for the first run of the program + optionallyMeasureToolsAtStart: false, // measure each tool used at the beginning of the program when block delete is turned off - this allows the operator to easily measure all tools before they are used for the first run of the program + toolBreakageTolerance: 0.1, // value for which tool break detection will raise an alarm + safeStartAllOperations: false, // write optional blocks at the beginning of all operations that include all commands to start program + fastToolChange: false, // skip spindle off, coolant off, and Z retract to make tool change quicker + useG95forTapping: false, // use IPR/MPR instead of IPM/MPM for tapping + useG73Retract: false, // use G73 Q K format for accumulated depth support + setting34: 1.0, // diameter used by control to calculate feed rates (INCH value) + useDPMFeeds: false // output DPM feeds instead of Inverse Time feeds +}; + +propertyDefinitions = { + writeMachine: {title:"Write machine", description:"Output the machine settings in the header of the code.", group:0, type:"boolean"}, + writeTools: {title:"Write tool list", description:"Output a tool list in the header of the code.", group:0, type:"boolean"}, + writeVersion: {title:"Write version", description:"Write the version number in the header of the code.", group:0, type:"boolean"}, + preloadTool: {title:"Preload tool", description:"Preloads the next tool at a tool change (if any).", type:"boolean"}, + chipTransport: {title:"Use chip transport", description:"Enable to turn on chip transport at start of program.", type:"boolean"}, + showSequenceNumbers: {title:"Use sequence numbers", description:"Use sequence numbers for each block of outputted code.", group:1, type:"boolean"}, + sequenceNumberStart: {title:"Start sequence number", description:"The number at which to start the sequence numbers.", group:1, type:"integer"}, + sequenceNumberIncrement: {title:"Sequence number increment", description:"The amount by which the sequence number is incremented by in each block.", group:1, type:"integer"}, + sequenceNumberOnlyOnToolChange: {title:"Block number only on tool change", description:"Specifies that block numbers should only be output at tool changes.", type:"boolean"}, + optionalStop: {title:"Optional stop", description:"Specifies that optional stops M1 should be output at tool changes.", type:"boolean"}, + separateWordsWithSpace: {title:"Separate words with space", description:"Adds spaces between words if 'yes' is selected.", type:"boolean"}, + useRadius: {title:"Radius arcs", description:"If yes is selected, arcs are output using radius values rather than IJK.", type:"boolean"}, + useParametricFeed: {title:"Parametric feed", description:"Parametric feed values based on movement type are output.", type:"boolean"}, + showNotes: {title:"Show notes", description:"Enable to output notes for operations.", type:"boolean"}, + useG0: {title:"Use G0", description:"Specifies that G0s should be used for rapid moves when moving along a single axis.", type:"boolean"}, + useG28: {title:"Use G28 instead of G53", description:"Specifies that machine retracts should be done using G28 instead of G53.", type:"boolean"}, + useSubroutines: {title:"Use subroutines", description:"Enables output of subroutines for each operation.", type:"boolean"}, + useSubroutinePatterns: {title:"Subroutines for patterns", description:"Enable output of subroutines for patterns.", type:"boolean"}, + useSubroutineCycles: {title:"Subroutines for cycles", description:"Enable output of subroutines for cycles.", type:"boolean"}, + useG187: {title:"Use G187", description:"Specifies that smoothing using G187 should be used.", type:"boolean"}, + homePositionCenter: {title:"Home position center", description:"Enable to center the part along X at the end of program for easy access. Requires a CNC with a moving table.", type:"boolean"}, + optionallyCycleToolsAtStart: {title:"Optionally cycle tools at start", description:"Cycle through each tool used at the beginning of the program when block delete is turned off.", type:"boolean"}, + optionallyMeasureToolsAtStart: {title:"Optionally measure tools at start", description:"Measure each tool used at the beginning of the program when block delete is turned off.", type:"boolean"}, + toolBreakageTolerance: {title:"Tool breakage tolerance", description:"Specifies the tolerance for which tool break detection will raise an alarm.", type:"spatial"}, + safeStartAllOperations: {title:"Safe start all operations", description:"Write optional blocks at the beginning of all operations that include all commands to start program.", type:"boolean"}, + fastToolChange: {title:"Fast tool change", description:"Skip spindle off, coolant off, and Z retract to make tool change quicker.", type:"boolean"}, + useG95forTapping: {title:"Use G95 for tapping", description:"use IPR/MPR instead of IPM/MPM for tapping", type:"boolean"}, + useG73Retract: {title:"G73 cycles include accumulated depth", description:"Use G73 Q K format for accumulated depth support.", type:"boolean"}, + setting34: {title:"Feed rate calculation diameter", description:"Defines the part diameter in inches that the control uses to calculate feed rates (Setting 34).", type:"spatial", range:[0.1, 9999.0]}, + useDPMFeeds: {title:"Rotary moves use IPM feeds", description:"Enable to output IPM feeds, disable for Inverse Time feeds with rotary axes moves.", type:"boolean"} +}; + +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}, + {id: COOLANT_THROUGH_TOOL, on: 88, off: 89}, + {id: COOLANT_AIR, on: 83, off: 84}, + {id: COOLANT_AIR_THROUGH_TOOL, on: 73, off: 74}, + {id: COOLANT_SUCTION}, + {id: COOLANT_FLOOD_MIST}, + {id: COOLANT_FLOOD_THROUGH_TOOL, on: [88, 8], off: [89, 9]}, + {id: COOLANT_OFF, off: 9} +]; + +// old machines only support 4 digits +var oFormat = createFormat({width:5, zeropad:true, decimals:0}); +var nFormat = createFormat({decimals:0}); + +var gFormat = createFormat({prefix:"G", decimals:0}); +var mFormat = createFormat({prefix:"M", decimals:0}); +var hFormat = createFormat({prefix:"H", decimals:0}); +var dFormat = createFormat({prefix:"D", decimals:0}); +var probe154Format = createFormat({decimals:0, zeropad:true, width:2}); + +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-1000 +var milliFormat = createFormat({decimals:0}); // milliseconds // range 1-9999 +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 pitchOutput = createVariable({prefix:"F", force:true}, pitchFormat); +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({force:true}, gFormat); // modal group 10 // G98-99 +var gRotationModal = createModal({}, gFormat); // modal group 16 // G68-G69 + +// fixed settings +var firstFeedParameter = 100; // the first variable to use with parametric feed +var forceResetWorkPlane = false; // enable to force reset of machine ABC on new orientation +var minimumCyclePoints = 5; // minimum number of points in cycle operation to consider for subprogram + +var WARNING_WORK_OFFSET = 0; + +var ANGLE_PROBE_NOT_SUPPORTED = 0; +var ANGLE_PROBE_USE_ROTATION = 1; +var ANGLE_PROBE_USE_CAXIS = 2; + +var SUB_UNKNOWN = 0; +var SUB_PATTERN = 1; +var SUB_CYCLE = 2; + +// collected state +var sequenceNumber; +var currentWorkOffset; +var optionalSection = false; +var forceSpindleSpeed = false; +var activeMovements; // do not use by default +var currentFeedId; +var maximumCircularRadiiDifference = toPreciseUnit(0.005, MM); +var maximumLineLength = 80; // the maximum number of charaters allowed in a line +var g68RotationMode = 0; +var angularProbingMode; +var subprograms = []; +var currentPattern = -1; +var firstPattern = false; +var currentSubprogram; +var lastSubprogram; +var initialSubprogramNumber = 90000; +var definedPatterns = new Array(); +var incrementalMode = false; +var saveShowSequenceNumbers; +var cycleSubprogramIsActive = false; +var patternIsActive = false; +var lastOperationComment = ""; +var incrementalSubprogram; +var retracted = false; // specifies that the tool has been retracted to the safe plane +var measureTool = false; +probeMultipleFeatures = true; + +// used to convert blocks to optional for safeStartAllOperations, might get used outside of onSection +var operationNeedsSafeStart = false; + +/** + Writes the specified block. +*/ +var skipBlock = false; +function writeBlock() { + var text = formatWords(arguments); + if (!text) { + return; + } + var maximumSequenceNumber = (properties.useSubroutines || properties.useSubroutinePatterns || + properties.useSubroutineCycles) ? initialSubprogramNumber : 99999; + if (properties.showSequenceNumbers) { + if (sequenceNumber >= maximumSequenceNumber) { + sequenceNumber = properties.sequenceNumberStart; + } + if (optionalSection || skipBlock) { + if (text) { + writeWords("/", "N" + sequenceNumber, text); + } + } else { + writeWords2("N" + sequenceNumber, arguments); + } + sequenceNumber += properties.sequenceNumberIncrement; + } else { + if (optionalSection || skipBlock) { + writeWords2("/", arguments); + } else { + writeWords(arguments); + } + } + skipBlock = false; +} + +/** + Writes the specified block - used for tool changes only. +*/ +function writeToolBlock() { + var show = properties.showSequenceNumbers; + properties.showSequenceNumbers = show || properties.sequenceNumberOnlyOnToolChange; + writeBlock(arguments); + properties.showSequenceNumbers = show; +} + +/** + Writes the specified optional block. +*/ +function writeOptionalBlock() { + skipBlock = true; + writeBlock(arguments); +} + +function formatComment(text) { + return "(" + String(text).replace(/[()]/g, "") + ")"; +} + +/** + Output a comment. +*/ +function writeComment(text) { + writeln(formatComment(text.substr(0, maximumLineLength - 2))); +} + +/** + Returns the matching HAAS tool type for the tool. +*/ +function getHaasToolType(toolType) { + switch (toolType) { + case TOOL_DRILL: + case TOOL_REAMER: + return 1; // drill + case TOOL_TAP_RIGHT_HAND: + case TOOL_TAP_LEFT_HAND: + return 2; // tap + case TOOL_MILLING_FACE: + case TOOL_MILLING_SLOT: + case TOOL_BORING_BAR: + return 3; // shell mill + case TOOL_MILLING_END_FLAT: + case TOOL_MILLING_END_BULLNOSE: + case TOOL_MILLING_TAPERED: + case TOOL_MILLING_DOVETAIL: + return 4; // end mill + case TOOL_DRILL_SPOT: + case TOOL_MILLING_CHAMFER: + case TOOL_DRILL_CENTER: + case TOOL_COUNTER_SINK: + case TOOL_COUNTER_BORE: + case TOOL_MILLING_THREAD: + case TOOL_MILLING_FORM: + return 5; // center drill + case TOOL_MILLING_END_BALL: + case TOOL_MILLING_LOLLIPOP: + return 6; // ball nose + case TOOL_PROBE: + return 7; // probe + default: + error(localize("Invalid HAAS tool type.")); + return -1; + } +} + +function getHaasProbingType(toolType, use9023) { + switch (getHaasToolType(toolType)) { + case 3: + case 4: + return (use9023 ? 23 : 1); // rotate + case 1: + case 2: + case 5: + case 6: + case 7: + return (use9023 ? 12 : 2); // non rotate + case 0: + return (use9023 ? 13 : 3); // rotate length and dia + default: + error(localize("Invalid HAAS tool type.")); + return -1; + } +} + +function writeToolCycleBlock(tool) { + writeOptionalBlock("T" + toolFormat.format(tool.number), mFormat.format(6)); // get tool + writeOptionalBlock(mFormat.format(0)); // wait for operator +} + +function writeToolMeasureBlock(tool) { + var writeFunction = measureTool ? writeBlock : writeOptionalBlock; + var comment = measureTool ? formatComment("MEASURE TOOL") : ""; + if (true) { // use Macro P9023 to measure tools + var probingType = getHaasProbingType(tool.type, true); + writeFunction( + gFormat.format(65), + "P9023", + "A" + probingType + ".", + "T" + toolFormat.format(tool.number), + conditional((probingType != 12), "H" + xyzFormat.format(tool.bodyLength + tool.holderLength)), + conditional((probingType != 12), "D" + xyzFormat.format(tool.diameter)), + comment + ); + } else { // use Macro P9995 to measure tools + writeFunction("T" + toolFormat.format(tool.number), mFormat.format(6)); // get tool + writeFunction( + gFormat.format(65), + "P9995", + "A0.", + "B" + getHaasToolType(tool.type) + ".", + "C" + getHaasProbingType(tool.type, false) + ".", + "T" + toolFormat.format(tool.number), + "E" + xyzFormat.format(tool.bodyLength + tool.holderLength), + "D" + xyzFormat.format(tool.diameter), + "K" + xyzFormat.format(0.1), + "I0.", + comment + ); // probe tool + } + measureTool = false; +} + +function onOpen() { + if (properties.useRadius) { + maximumCircularSweep = toRad(90); // avoid potential center calculation errors for CNC + } + if (properties.sequenceNumberOnlyOnToolChange) { + properties.showSequenceNumbers = false; + } + + gRotationModal.format(69); // Default to G69 Rotation Off + + if (false) { // note: setup your machine here + var aAxis = createAxis({coordinate:0, table:true, axis:[-1, 0, 0], range:[-30 - 0.0001, 120 + 0.0001], preference:1}); + var bAxis = createAxis({coordinate:1, table:true, axis:[0, 0, 1], range:[-999.999, 999.999]}); + machineConfiguration = new MachineConfiguration(aAxis, bAxis); + + setMachineConfiguration(machineConfiguration); + optimizeMachineAngles2(1); // map tip mode + } + + if (!machineConfiguration.isMachineCoordinate(0)) { + aOutput.disable(); + } + if (!machineConfiguration.isMachineCoordinate(1)) { + bOutput.disable(); + } + if (!machineConfiguration.isMachineCoordinate(2)) { + cOutput.disable(); + } + + if (highFeedrate <= 0) { + error(localize("You must set 'highFeedrate' because axes are not synchronized for rapid traversal.")); + return; + } + + if (!properties.separateWordsWithSpace) { + setWordSeparator(""); + } + saveShowSequenceNumbers = properties.showSequenceNumbers; + sequenceNumber = properties.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 <= 99999))) { + error(localize("Program number is out of range.")); + return; + } + writeln( + "O" + oFormat.format(programId) + + conditional(programComment, " " + formatComment(programComment.substr(0, maximumLineLength - 2 - ("O" + oFormat.format(programId)).length - 1))) + ); + lastSubprogram = (initialSubprogramNumber - 1); + } else { + error(localize("Program name has not been specified.")); + return; + } + + if (properties.useG0) { + writeComment(localize("Using G0 which travels along dogleg path.")); + } else { + writeComment(subst(localize("Using high feed G1 F%1 instead of G0."), feedFormat.format(highFeedrate))); + } + + if (properties.writeVersion) { + if ((typeof getHeaderVersion == "function") && getHeaderVersion()) { + writeComment(localize("post version") + ": " + getHeaderVersion()); + } + if ((typeof getHeaderDate == "function") && getHeaderDate()) { + writeComment(localize("post modified") + ": " + getHeaderDate()); + } + } + + // dump machine configuration + var vendor = machineConfiguration.getVendor(); + var model = machineConfiguration.getModel(); + var description = machineConfiguration.getDescription(); + + if (properties.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); + } + } + + // dump tool information + if (properties.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); + } + } + } + + // optionally cycle through all tools + if (properties.optionallyCycleToolsAtStart || properties.optionallyMeasureToolsAtStart) { + var tools = getToolTable(); + if (tools.getNumberOfTools() > 0) { + writeln(""); + + writeOptionalBlock(mFormat.format(0), formatComment(localize("Read note"))); // wait for operator + writeComment(localize("With BLOCK DELETE turned off each tool will cycle through")); + writeComment(localize("the spindle to verify that the correct tool is in the tool magazine")); + if (properties.optionallyMeasureToolsAtStart) { + writeComment(localize("and to automatically measure it")); + } + writeComment(localize("Once the tools are verified turn BLOCK DELETE on to skip verification")); + + for (var i = 0; i < tools.getNumberOfTools(); ++i) { + var tool = tools.getTool(i); + if (properties.optionallyMeasureToolsAtStart && (tool.type == TOOL_PROBE)) { + continue; + } + 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"); + } + comment += " - " + getToolTypeName(tool.type); + writeComment(comment); + if (properties.optionallyMeasureToolsAtStart) { + writeToolMeasureBlock(tool); + } else { + writeToolCycleBlock(tool); + } + } + } + writeln(""); + } + + if (false /*properties.useDynamicWorkOffset*/) { + var failed = false; + var dynamicWCSs = {}; + for (var i = 0; i < getNumberOfSections(); ++i) { + var section = getSection(i); + var description = section.hasParameter("operation-comment") ? section.getParameter("operation-comment") : ("#" + (i + 1)); + if (!section.hasDynamicWorkOffset()) { + error(subst(localize("Dynamic work offset has not been set for operation '%1'."), description)); + failed = true; + } + + var o = section.getDynamicWCSOrigin(); + var p = section.getDynamicWCSPlane(); + if (dynamicWCSs[section.getDynamicWorkOffset()]) { + if ((Vector.diff(o, dynamicWCSs[section.getDynamicWorkOffset()].origin).length > 1e-9) || + (Matrix.diff(p, dynamicWCSs[section.getDynamicWorkOffset()].plane).n1 > 1e-9)) { + error(subst(localize("Dynamic WCS mismatch for operation '%1'."), description)); + failed = true; + } + } else { + dynamicWCSs[section.getDynamicWorkOffset()] = {origin:o, plane:p}; + } + } + if (failed) { + return; + } + } + + 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 and feed per min + writeBlock(gAbsIncModal.format(90), gFeedModeModal.format(94), gPlaneModal.format(17)); + + switch (unit) { + case IN: + writeBlock(gUnitModal.format(20)); + break; + case MM: + writeBlock(gUnitModal.format(21)); + break; + } + + if (properties.chipTransport) { + onCommand(COMMAND_START_CHIP_TRANSPORT); + } + // Probing Surface Inspection + if (typeof inspectionWriteVariables == "function") { + inspectionWriteVariables(); + } +} + +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(); +} + +function writeG187() { + if (hasParameter("operation-strategy") && (getParameter("operation-strategy") == "drill")) { + writeBlock(gFormat.format(187)); // reset G187 setting to machine default + } else if (hasParameter("operation:tolerance")) { + var tolerance = Math.max(getParameter("operation:tolerance"), 0); + if (tolerance > 0) { + var stockToLeaveThreshold = toUnit(0.1, MM); + var stockToLeave = 0; + var verticalStockToLeave = 0; + if (hasParameter("operation:stockToLeave")) { + stockToLeave = xyzFormat.getResultingValue(getParameter("operation:stockToLeave")); + } + if (hasParameter("operation:verticalStockToLeave")) { + verticalStockToLeave = xyzFormat.getResultingValue(getParameter("operation:verticalStockToLeave")); + } + + var workMode; + if (((stockToLeave > stockToLeaveThreshold) && (verticalStockToLeave > stockToLeaveThreshold)) || + (hasParameter("operation:strategy") && getParameter("operation:strategy") == "face")) { + workMode = 1; // roughing + } else { + // if ((stockToLeave != 0) || (verticalStockToLeave != 0)) { + if ((stockToLeave > 0) || (verticalStockToLeave > 0)) { + workMode = 2; // default + } else { + workMode = 3; // fine + } + } + writeBlock(gFormat.format(187), "P" + workMode); // set tolerance mode + // writeBlock(gFormat.format(187), "P" + workMode, "E" + xyzFormat.format(tolerance)); // set tolerance mode + } else { + writeBlock(gFormat.format(187)); // reset G187 setting to machine default + } + } else { + writeBlock(gFormat.format(187)); // reset G187 setting to machine default + } +} + +function FeedContext(id, description, feed) { + this.id = id; + this.description = description; + this.feed = feed; +} + +function getFeed(f) { + if (useInverseTimeFeed) { + forceFeed(); + } + 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; + +function forceWorkPlane() { + currentWorkPlaneABC = undefined; +} + +function defineWorkPlane(_section, _setWorkPlane) { + var abc = new Vector(0, 0, 0); + if (machineConfiguration.isMultiAxisConfiguration()) { // use 5-axis indexing for multi-axis mode + // set working plane after datum shift + + if (_section.isMultiAxis()) { + cancelTransformation(); + abc = _section.getInitialToolAxisABC(); + if (_setWorkPlane) { + if (!retracted) { + writeRetract(Z); + } + forceWorkPlane(); + onCommand(COMMAND_UNLOCK_MULTI_AXIS); + 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 { + abc = getWorkPlaneMachineABC(_section.workPlane, _setWorkPlane); + if (_setWorkPlane) { + setWorkPlane(abc); + } + } + } else { // pure 3D + var remaining = _section.workPlane; + if (!isSameDirection(remaining.forward, new Vector(0, 0, 1))) { + error(localize("Tool orientation is not supported.")); + return abc; + } + setRotation(remaining); + } + return abc; +} + +function setWorkPlane(abc) { + if (!machineConfiguration.isMultiAxisConfiguration()) { + return; // ignore + } + + var _skipBlock = false; + if (!((currentWorkPlaneABC == undefined) || + abcFormat.areDifferent(abc.x, currentWorkPlaneABC.x) || + abcFormat.areDifferent(abc.y, currentWorkPlaneABC.y) || + abcFormat.areDifferent(abc.z, currentWorkPlaneABC.z))) { + if (operationNeedsSafeStart) { + _skipBlock = true; + } else { + return; // no change + } + } + + skipBlock = _skipBlock; + onCommand(COMMAND_UNLOCK_MULTI_AXIS); + + gMotionModal.reset(); + skipBlock = _skipBlock; + 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)) + ); + + skipBlock = _skipBlock; + 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, _setWorkPlane) { + 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); + if (_setWorkPlane) { + 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; + if (tcp) { + setRotation(W); // TCP mode + } else { + var O = machineConfiguration.getOrientation(abc); + var R = machineConfiguration.getRemainingOrientation(abc, W); + setRotation(R); + } + + return abc; +} + +function isProbeOperation() { + return hasParameter("operation-strategy") && ((getParameter("operation-strategy") == "probe" || getParameter("operation-strategy") == "probe_geometry")); +} + +function isInspectionOperation(section) { + return section.hasParameter("operation-strategy") && (section.getParameter("operation-strategy") == "inspectSurface"); +} + +var probeOutputWorkOffset = 1; + +function onParameter(name, value) { + if (name == "probe-output-work-offset") { + probeOutputWorkOffset = (value > 0) ? value : 1; + } +} + +/** Returns true if the spatial vectors are significantly different. */ +function areSpatialVectorsDifferent(_vector1, _vector2) { + return (xyzFormat.getResultingValue(_vector1.x) != xyzFormat.getResultingValue(_vector2.x)) || + (xyzFormat.getResultingValue(_vector1.y) != xyzFormat.getResultingValue(_vector2.y)) || + (xyzFormat.getResultingValue(_vector1.z) != xyzFormat.getResultingValue(_vector2.z)); +} + +/** Returns true if the spatial boxes are a pure translation. */ +function areSpatialBoxesTranslated(_box1, _box2) { + return !areSpatialVectorsDifferent(Vector.diff(_box1[1], _box1[0]), Vector.diff(_box2[1], _box2[0])) && + !areSpatialVectorsDifferent(Vector.diff(_box2[0], _box1[0]), Vector.diff(_box2[1], _box1[1])); +} + +/** Returns true if the spatial boxes are same. */ +function areSpatialBoxesSame(_box1, _box2) { + return !areSpatialVectorsDifferent(_box1[0], _box2[0]) && !areSpatialVectorsDifferent(_box1[1], _box2[1]); +} + +function subprogramDefine(_initialPosition, _abc, _retracted, _zIsOutput) { + // convert patterns into subprograms + var usePattern = false; + patternIsActive = false; + if (currentSection.isPatterned && currentSection.isPatterned() && properties.useSubroutinePatterns) { + currentPattern = currentSection.getPatternId(); + firstPattern = true; + for (var i = 0; i < definedPatterns.length; ++i) { + if ((definedPatterns[i].patternType == SUB_PATTERN) && (currentPattern == definedPatterns[i].patternId)) { + currentSubprogram = definedPatterns[i].subProgram; + usePattern = definedPatterns[i].validPattern; + firstPattern = false; + break; + } + } + + if (firstPattern) { + // determine if this is a valid pattern for creating a subprogram + usePattern = subprogramIsValid(currentSection, currentPattern, SUB_PATTERN); + if (usePattern) { + currentSubprogram = ++lastSubprogram; + } + definedPatterns.push({ + patternType: SUB_PATTERN, + patternId: currentPattern, + subProgram: currentSubprogram, + validPattern: usePattern, + initialPosition: _initialPosition, + finalPosition: _initialPosition + }); + } + + if (usePattern) { + // make sure Z-position is output prior to subprogram call + if (!_retracted && !_zIsOutput) { + writeBlock(gMotionModal.format(0), zOutput.format(_initialPosition.z)); + } + + // call subprogram + writeBlock(mFormat.format(97), "P" + nFormat.format(currentSubprogram)); + patternIsActive = true; + + if (firstPattern) { + subprogramStart(_initialPosition, _abc, incrementalSubprogram); + } else { + skipRemainingSection(); + setCurrentPosition(getFramePosition(currentSection.getFinalPosition())); + } + } + } + + // Output cycle operation as subprogram + if (!usePattern && properties.useSubroutineCycles && currentSection.doesStrictCycle && + (currentSection.getNumberOfCycles() == 1) && currentSection.getNumberOfCyclePoints() >= minimumCyclePoints) { + var finalPosition = getFramePosition(currentSection.getFinalPosition()); + currentPattern = currentSection.getNumberOfCyclePoints(); + firstPattern = true; + for (var i = 0; i < definedPatterns.length; ++i) { + if ((definedPatterns[i].patternType == SUB_CYCLE) && (currentPattern == definedPatterns[i].patternId) && + !areSpatialVectorsDifferent(_initialPosition, definedPatterns[i].initialPosition) && + !areSpatialVectorsDifferent(finalPosition, definedPatterns[i].finalPosition)) { + currentSubprogram = definedPatterns[i].subProgram; + usePattern = definedPatterns[i].validPattern; + firstPattern = false; + break; + } + } + + if (firstPattern) { + // determine if this is a valid pattern for creating a subprogram + usePattern = subprogramIsValid(currentSection, currentPattern, SUB_CYCLE); + if (usePattern) { + currentSubprogram = ++lastSubprogram; + } + definedPatterns.push({ + patternType: SUB_CYCLE, + patternId: currentPattern, + subProgram: currentSubprogram, + validPattern: usePattern, + initialPosition: _initialPosition, + finalPosition: finalPosition + }); + } + cycleSubprogramIsActive = usePattern; + } + + // Output each operation as a subprogram + if (!usePattern && properties.useSubroutines) { + currentSubprogram = ++lastSubprogram; + writeBlock(mFormat.format(97), "P" + nFormat.format(currentSubprogram)); + firstPattern = true; + subprogramStart(_initialPosition, _abc, false); + } +} + +function subprogramStart(_initialPosition, _abc, _incremental) { + redirectToBuffer(); + var comment = ""; + if (hasParameter("operation-comment")) { + comment = getParameter("operation-comment"); + } + writeln( + "N" + nFormat.format(currentSubprogram) + + conditional(comment, formatComment(comment.substr(0, maximumLineLength - 2 - 6 - 1))) + ); + properties.showSequenceNumbers = false; + if (_incremental) { + setIncrementalMode(_initialPosition, _abc); + } + gPlaneModal.reset(); + gMotionModal.reset(); +} + +function subprogramEnd() { + if (firstPattern) { + writeBlock(mFormat.format(99)); + writeln(""); + subprograms += getRedirectionBuffer(); + } + forceAny(); + firstPattern = false; + properties.showSequenceNumbers = saveShowSequenceNumbers; + closeRedirection(); +} + +function subprogramIsValid(_section, _patternId, _patternType) { + var sectionId = _section.getId(); + var numberOfSections = getNumberOfSections(); + var validSubprogram = _patternType != SUB_CYCLE; + + var masterPosition = new Array(); + masterPosition[0] = getFramePosition(_section.getInitialPosition()); + masterPosition[1] = getFramePosition(_section.getFinalPosition()); + var tempBox = _section.getBoundingBox(); + var masterBox = new Array(); + masterBox[0] = getFramePosition(tempBox[0]); + masterBox[1] = getFramePosition(tempBox[1]); + + var rotation = getRotation(); + var translation = getTranslation(); + incrementalSubprogram = undefined; + + for (var i = 0; i < numberOfSections; ++i) { + var section = getSection(i); + if (section.getId() != sectionId) { + defineWorkPlane(section, false); + // check for valid pattern + if (_patternType == SUB_PATTERN) { + if (section.getPatternId() == _patternId) { + var patternPosition = new Array(); + patternPosition[0] = getFramePosition(section.getInitialPosition()); + patternPosition[1] = getFramePosition(section.getFinalPosition()); + tempBox = section.getBoundingBox(); + var patternBox = new Array(); + patternBox[0] = getFramePosition(tempBox[0]); + patternBox[1] = getFramePosition(tempBox[1]); + + if (areSpatialBoxesSame(masterPosition, patternPosition) && areSpatialBoxesSame(masterBox, patternBox)) { + incrementalSubprogram = incrementalSubprogram ? incrementalSubprogram : false; + } else if (!areSpatialBoxesTranslated(masterPosition, patternPosition) || !areSpatialBoxesTranslated(masterBox, patternBox)) { + validSubprogram = false; + break; + } else { + incrementalSubprogram = true; + } + } + + // check for valid cycle operation + } else if (_patternType == SUB_CYCLE) { + if ((section.getNumberOfCyclePoints() == _patternId) && (section.getNumberOfCycles() == 1)) { + var patternInitial = getFramePosition(section.getInitialPosition()); + var patternFinal = getFramePosition(section.getFinalPosition()); + if (!areSpatialVectorsDifferent(patternInitial, masterPosition[0]) && !areSpatialVectorsDifferent(patternFinal, masterPosition[1])) { + validSubprogram = true; + break; + } + } + } + } + } + setRotation(rotation); + setTranslation(translation); + return (validSubprogram); +} + +function setAxisMode(_format, _output, _prefix, _value, _incr) { + var i = _output.isEnabled(); + _output = _incr ? createIncrementalVariable({prefix: _prefix}, _format) : createVariable({prefix: _prefix}, _format); + _output.format(_value); + _output.format(_value); + i = i ? _output.enable() : _output.disable(); + return _output; +} + +function setIncrementalMode(xyz, abc) { + xOutput = setAxisMode(xyzFormat, xOutput, "X", xyz.x, true); + yOutput = setAxisMode(xyzFormat, yOutput, "Y", xyz.y, true); + zOutput = setAxisMode(xyzFormat, zOutput, "Z", xyz.z, true); + aOutput = setAxisMode(abcFormat, aOutput, "A", abc.x, true); + bOutput = setAxisMode(abcFormat, bOutput, "B", abc.y, true); + cOutput = setAxisMode(abcFormat, cOutput, "C", abc.z, true); + gAbsIncModal.reset(); + writeBlock(gAbsIncModal.format(91)); + incrementalMode = true; +} + +function setAbsoluteMode(xyz, abc) { + if (incrementalMode) { + xOutput = setAxisMode(xyzFormat, xOutput, "X", xyz.x, false); + yOutput = setAxisMode(xyzFormat, yOutput, "Y", xyz.y, false); + zOutput = setAxisMode(xyzFormat, zOutput, "Z", xyz.z, false); + aOutput = setAxisMode(abcFormat, aOutput, "A", abc.x, false); + bOutput = setAxisMode(abcFormat, bOutput, "B", abc.y, false); + cOutput = setAxisMode(abcFormat, cOutput, "C", abc.z, false); + gAbsIncModal.reset(); + writeBlock(gAbsIncModal.format(90)); + incrementalMode = false; + } +} + +function onSection() { + var forceToolAndRetract = optionalSection && !currentSection.isOptional(); + optionalSection = currentSection.isOptional(); + + var insertToolCall = isFirstSection() || + currentSection.getForceToolChange && currentSection.getForceToolChange() || + (tool.number != getPreviousSection().getTool().number); + + retracted = false; + var zIsOutput = false; // true if the Z-position has been output, used for patterns + + 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 + + operationNeedsSafeStart = properties.safeStartAllOperations && !isFirstSection(); + + if (insertToolCall || operationNeedsSafeStart) { + nullProbeAngle(true); + if (properties.fastToolChange && !isProbeOperation()) { + currentCoolantMode = COOLANT_OFF; + } else if (insertToolCall) { // no coolant off command if safe start operation + onCommand(COMMAND_COOLANT_OFF); + } + } + + if ((insertToolCall && !properties.fastToolChange) || newWorkOffset || newWorkPlane || toolChecked) { + + // stop spindle before retract during tool change + if (insertToolCall && !isFirstSection() && !toolChecked && !properties.fastToolChange) { + onCommand(COMMAND_STOP_SPINDLE); + } + + // retract to safe plane + writeRetract(Z); + writeBlock(gAbsIncModal.format(90)); + zOutput.reset(); + + if (forceResetWorkPlane && newWorkPlane) { + forceWorkPlane(); + setWorkPlane(new Vector(0, 0, 0)); // reset working plane + } + } + + if (hasParameter("operation-comment")) { + var comment = getParameter("operation-comment"); + if (comment && ((comment !== lastOperationComment) || !patternIsActive || insertToolCall)) { + writeln(""); + writeComment(comment); + lastOperationComment = comment; + } else if (!patternIsActive || insertToolCall) { + writeln(""); + } + } else { + writeln(""); + } + + if (properties.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); + } + } + } + } + + defineWorkPlane(currentSection, false); + var initialPosition = getFramePosition(currentSection.getInitialPosition()); + forceAny(); + if (!retracted && !insertToolCall) { + if (getCurrentPosition().z < initialPosition.z) { + writeBlock(gMotionModal.format(0), zOutput.format(initialPosition.z)); + zIsOutput = true; + } + } + + if (operationNeedsSafeStart) { + if (!retracted) { + skipBlock = true; + writeRetract(Z); + } + } + + if (insertToolCall || operationNeedsSafeStart) { + if (insertToolCall) { + forceWorkPlane(); + } + + if (!isFirstSection() && properties.optionalStop && insertToolCall) { + onCommand(COMMAND_OPTIONAL_STOP); + } + + if ((tool.number > 200 && tool.number < 1000) || tool.number > 9999) { + warning(localize("Tool number out of range.")); + } + + skipBlock = !insertToolCall; + writeToolBlock( + "T" + toolFormat.format(tool.number), + mFormat.format(6) + ); + + writeRetract(Z);//Retracts after tool change + + if (tool.comment) { + writeComment(tool.comment); + } + if (measureTool) { + writeToolMeasureBlock(tool); + } + 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") + "=" + xyzFormat.format(zRange.getMinimum())); + } + } + } + + // activate those two coolant modes before the spindle is turned on + if ((tool.coolant == COOLANT_THROUGH_TOOL) || (tool.coolant == COOLANT_AIR_THROUGH_TOOL) || (tool.coolant == COOLANT_FLOOD_THROUGH_TOOL)) { + if (!isFirstSection() && !insertToolCall && (currentCoolantMode != tool.coolant)) { + onCommand(COMMAND_STOP_SPINDLE); + forceSpindleSpeed = true; + } + setCoolant(tool.coolant); + } else if ((currentCoolantMode == COOLANT_THROUGH_TOOL) || (currentCoolantMode == COOLANT_AIR_THROUGH_TOOL) || (currentCoolantMode == COOLANT_FLOOD_THROUGH_TOOL)) { + onCommand(COMMAND_STOP_SPINDLE); + setCoolant(COOLANT_OFF); + forceSpindleSpeed = true; + } + + if (toolChecked) { + forceSpindleSpeed = true; // spindle must be restarted if tool is checked without a tool change + toolChecked = false; // state of tool is not known at the beginning of a section since it could be broken for the previous section + } + var spindleChanged = !isInspectionOperation(currentSection) && !isProbeOperation() && + (insertToolCall || forceSpindleSpeed || isFirstSection() || + (rpmFormat.areDifferent(spindleSpeed, sOutput.getCurrent())) || + (tool.clockwise != getPreviousSection().getTool().clockwise)); + if (spindleChanged || (operationNeedsSafeStart && !isInspectionOperation(currentSection) && !isProbeOperation())) { + forceSpindleSpeed = false; + + if (spindleSpeed < 1) { + error(localize("Spindle speed out of range.")); + return; + } + if (spindleSpeed > 99999) { + warning(localize("Spindle speed exceeds maximum value.")); + } + skipBlock = !spindleChanged; + writeBlock( + sOutput.format(spindleSpeed), mFormat.format(tool.clockwise ? 3 : 4) + ); + } + + if (properties.useParametricFeed && + hasParameter("operation-strategy") && + (getParameter("operation-strategy") != "drill") && + !(useInverseTimeFeed && currentSection.isMultiAxis()) && // 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; + } + + // wcs + if (insertToolCall || operationNeedsSafeStart) { // force work offset when changing tool + currentWorkOffset = undefined; + skipBlock = operationNeedsSafeStart && !newWorkOffset && !insertToolCall; + } + var workOffset = currentSection.workOffset; + if (workOffset == 0) { + warningOnce(localize("Work offset has not been specified. Using G54 as WCS."), WARNING_WORK_OFFSET); + workOffset = 1; + } + if (workOffset > 0) { + if (workOffset > 6) { + var code = workOffset - 6; + if (code > 99) { + error(localize("Work offset out of range.")); + return; + } + if (workOffset != currentWorkOffset) { + if (insertToolCall) { + forceWorkPlane(); + } + writeBlock(gFormat.format(154), "P" + code); + currentWorkOffset = workOffset; + } + } else { + if (workOffset != currentWorkOffset) { + if (insertToolCall) { + forceWorkPlane(); + } + writeBlock(gFormat.format(53 + workOffset)); // G54->G59 + currentWorkOffset = workOffset; + } + } + } + + if (g68RotationMode != 0 && (insertToolCall || operationNeedsSafeStart || gRotationModal.getCurrent() == 69)) { + setProbingAngle(); + } + + forceXYZ(); + + var abc = defineWorkPlane(currentSection, true); + + // set coolant after we have positioned at Z + setCoolant(tool.coolant); + + forceAny(); + gMotionModal.reset(); + + if (properties.useG187) { + writeG187(); + } + + if (insertToolCall || retracted || operationNeedsSafeStart) { + var _skipBlock = !(insertToolCall || retracted); + var lengthOffset = tool.lengthOffset; + if ((lengthOffset > 200 && lengthOffset < 1000) || lengthOffset > 9999) { + error(localize("Length offset out of range.")); + return; + } + + gMotionModal.reset(); + writeBlock(gPlaneModal.format(17)); + + if (!machineConfiguration.isHeadConfiguration()) { + skipBlock = _skipBlock; + writeBlock( + gAbsIncModal.format(90), + gMotionModal.format(0), xOutput.format(initialPosition.x), yOutput.format(initialPosition.y) + ); + skipBlock = _skipBlock; + writeBlock( + gMotionModal.format(0), + gFormat.format(43), + zOutput.format(initialPosition.z), + hFormat.format(lengthOffset) + ); + } else { + skipBlock = _skipBlock; + writeBlock( + gAbsIncModal.format(90), + gMotionModal.format(0), + gFormat.format(43), + xOutput.format(initialPosition.x), + yOutput.format(initialPosition.y), + zOutput.format(initialPosition.z), + hFormat.format(lengthOffset) + ); + } + zIsOutput = true; + if (_skipBlock) { + forceXYZ(); + var x = xOutput.format(initialPosition.x); + var y = yOutput.format(initialPosition.y); + if (!properties.useG0 && x && y) { + // axes are not synchronized + writeBlock(gAbsIncModal.format(90), gMotionModal.format(1), x, y, getFeed(highFeedrate)); + } else { + writeBlock(gAbsIncModal.format(90), gMotionModal.format(0), x, y); + } + } + + gMotionModal.reset(); + } else { + var x = xOutput.format(initialPosition.x); + var y = yOutput.format(initialPosition.y); + if (!properties.useG0 && x && y) { + // axes are not synchronized + writeBlock(gAbsIncModal.format(90), gMotionModal.format(1), x, y, getFeed(highFeedrate)); + } else { + writeBlock(gAbsIncModal.format(90), gMotionModal.format(0), x, y); + } + } + + if (insertToolCall || operationNeedsSafeStart) { + if (properties.preloadTool) { + var nextTool = getNextTool(tool.number); + if (nextTool) { + skipBlock = !insertToolCall; + writeBlock("T" + toolFormat.format(nextTool.number)); + } else { + // preload first tool + var section = getSection(0); + var firstToolNumber = section.getTool().number; + if (tool.number != firstToolNumber) { + skipBlock = !insertToolCall; + writeBlock("T" + toolFormat.format(firstToolNumber)); + } + } + } + } + + if (isProbeOperation()) { + if (g68RotationMode != 0) { + error(localize("You cannot probe while G68 Rotation is in effect.")); + return; + } + angularProbingMode = getAngularProbingMode(); + writeBlock(gFormat.format(65), "P" + 9832); // spin the probe on + } + if (isInspectionOperation(currentSection) && (typeof inspectionProcessSectionStart == "function")) { + inspectionProcessSectionStart(); + } + // define subprogram + subprogramDefine(initialPosition, abc, retracted, zIsOutput); +} + +function onDwell(seconds) { + if (seconds > 99999.999) { + warning(localize("Dwelling time is out of range.")); + } + seconds = clamp(0.001, seconds, 99999.999); + writeBlock(gFeedModeModal.format(94), gFormat.format(4), "P" + milliFormat.format(seconds * 1000)); +} + +function onSpindleSpeed(spindleSpeed) { + writeBlock(sOutput.format(spindleSpeed)); +} + +function onCycle() { + writeBlock(gPlaneModal.format(17)); +} + +function getCommonCycle(x, y, z, r, c) { + forceXYZ(); // force xyz on first drill hole of any cycle + if (incrementalMode) { + zOutput.format(c); + return [xOutput.format(x), yOutput.format(y), + "Z" + xyzFormat.format(z - r), + "R" + xyzFormat.format(r - c)]; + } else { + return [xOutput.format(x), yOutput.format(y), + zOutput.format(z), + "R" + xyzFormat.format(r)]; + } +} + +function setCyclePosition(_position) { + switch (gPlaneModal.getCurrent()) { + case 17: // XY + zOutput.format(_position); + break; + case 18: // ZX + yOutput.format(_position); + break; + case 19: // YZ + xOutput.format(_position); + break; + } +} + +/** Convert approach to sign. */ +function approach(value) { + validate((value == "positive") || (value == "negative"), "Invalid approach."); + return (value == "positive") ? 1 : -1; +} + +function getRotationalAxis() { + if (machineConfiguration.getAxisU().isEnabled() && + isSameDirection((machineConfiguration.getAxisU().getAxis()).getAbsolute(), new Vector(0, 0, 1)) && + machineConfiguration.getAxisU().isTable()) { + return (machineConfiguration.getAxisU().getCoordinate()); + } else if (machineConfiguration.getAxisV().isEnabled() && + isSameDirection((machineConfiguration.getAxisV().getAxis()).getAbsolute(), new Vector(0, 0, 1)) && + machineConfiguration.getAxisV().isTable()) { + return (machineConfiguration.getAxisV().getCoordinate()); + } else if (machineConfiguration.getAxisW().isEnabled() && + isSameDirection((machineConfiguration.getAxisW().getAxis()).getAbsolute(), new Vector(0, 0, 1)) && + machineConfiguration.getAxisW().isTable()) { + return (machineConfiguration.getAxisW().getCoordinate()); + } else { + return -1; + } +} + +/** + Determine if angular probing is supported. +*/ +var rotationalAxis = -1; +var isWCSProbing = false; + +function getAngularProbingMode() { + rotationalAxis = getRotationalAxis(); + isWCSProbing = (hasParameter("operation-strategy") && (getParameter("operation-strategy") == "probe")); + if (rotationalAxis != -1) { + return (ANGLE_PROBE_USE_CAXIS); + } else if (machineConfiguration.getNumberOfAxes() < 5 || is3D()) { + return (ANGLE_PROBE_USE_ROTATION); + } else { + return (ANGLE_PROBE_NOT_SUPPORTED); + } +} + +/** + Output rotation offset based on angular probing cycle. +*/ +function setProbingAngle() { + if (((g68RotationMode == 1) || (g68RotationMode == 2)) && isWCSProbing) { // Rotate coordinate system for Angle Probing + if (angularProbingMode == ANGLE_PROBE_USE_ROTATION) { + gRotationModal.reset(); + gAbsIncModal.reset(); + var xCode = (g68RotationMode == 1) ? "X0" : "X[#185]"; + var yCode = (g68RotationMode == 1) ? "Y0" : "Y[#186]"; + writeBlock(gRotationModal.format(68), gAbsIncModal.format(90), xCode, yCode, "R[#194]"); + } else if (angularProbingMode == ANGLE_PROBE_USE_CAXIS) { + var workOffset = probeOutputWorkOffset ? probeOutputWorkOffset : currentWorkOffset; + if (workOffset > 6) { + error(localize("Angle Probing only supports work offsets 1-6.")); + return; + } + var param = 5200 + workOffset * 20 + rotationalAxis + 4; + writeBlock("#" + param + " = " + "[#" + param + " + #194]"); + g68RotationMode = 0; + } else { + error(localize("Angular Probing is not supported for this machine configuration.")); + return; + } + } +} + +function protectedProbeMove(_cycle, x, y, z) { + var _x = xOutput.format(x); + var _y = yOutput.format(y); + var _z = zOutput.format(z); + if (_z && z >= getCurrentPosition().z) { + writeBlock(gFormat.format(65), "P" + 9810, _z, getFeed(cycle.feedrate)); // protected positioning move + } + if (_x || _y) { + writeBlock(gFormat.format(65), "P" + 9810, _x, _y, getFeed(highFeedrate)); // protected positioning move + } + if (_z && z < getCurrentPosition().z) { + writeBlock(gFormat.format(65), "P" + 9810, _z, getFeed(cycle.feedrate)); // protected positioning move + } +} + +/** + Null the probing angle when needed. +*/ +function nullProbeAngle(force) { + var cycleType = currentSection.getFirstCycle(); + if (g68RotationMode != 0) { + writeBlock(gRotationModal.format(69)); + } else if (force && cycleType.indexOf("probing") != -1) { + gRotationModal.reset(); + forceXYZ(); + writeBlock(gRotationModal.format(69)); + } +} + +function onCyclePoint(x, y, z) { + if (isInspectionOperation(currentSection) && (typeof inspectionCycleInspect == "function")) { + inspectionCycleInspect(cycle, x, y, z); + return; + } + if (!isSameDirection(getRotation().forward, new Vector(0, 0, 1))) { + expandCyclePoint(x, y, z); + return; + } + var probeWorkOffsetCode; + if (isProbeOperation()) { + if (!isSameDirection(currentSection.workPlane.forward, new Vector(0, 0, 1)) && (!cycle.probeMode || (cycle.probeMode == 0))) { + error(localize("Updating WCS / work offset using probing is only supported by the CNC in the WCS frame.")); + return; + } + protectedProbeMove(cycle, x, y, z); + + var workOffset = probeOutputWorkOffset ? probeOutputWorkOffset : currentWorkOffset; + if (workOffset > 99) { + error(localize("Work offset is out of range.")); + return; + } else if (workOffset > 6) { + probeWorkOffsetCode = "154." + probe154Format.format(workOffset - 6); + } else { + probeWorkOffsetCode = workOffset + "."; // G54->G59 + } + } + + var forceCycle = false; + switch (cycleType) { + case "tapping-with-chip-breaking": + case "left-tapping-with-chip-breaking": + case "right-tapping-with-chip-breaking": + forceCycle = true; + if (!isFirstCyclePoint()) { + writeBlock(gCycleModal.format(80)); + gMotionModal.reset(); + } + } + if (forceCycle || isFirstCyclePoint() || isProbeOperation()) { + if (!isProbeOperation()) { + // return to initial Z which is clearance plane and set absolute mode + repositionToCycleClearance(cycle, x, y, z); + } + + var F = cycle.feedrate; + var P = !cycle.dwell ? 0 : clamp(1, cycle.dwell * 1000, 99999999); // in milliseconds + + switch (cycleType) { + case "drilling": + writeBlock( + gRetractModal.format(98), gCycleModal.format(81), + getCommonCycle(x, y, z, cycle.retract, cycle.clearance), + feedOutput.format(F) + ); + break; + case "counter-boring": + if (P > 0) { + writeBlock( + gRetractModal.format(98), gCycleModal.format(82), + getCommonCycle(x, y, z, cycle.retract, cycle.clearance), + "P" + milliFormat.format(P), // not optional + feedOutput.format(F) + ); + } else { + writeBlock( + gRetractModal.format(98), gCycleModal.format(81), + getCommonCycle(x, y, z, cycle.retract, cycle.clearance), + feedOutput.format(F) + ); + } + break; + case "chip-breaking": + if ((!properties.useG73Retract && (cycle.accumulatedDepth < cycle.depth)) || + (properties.useG73Retract && (cycle.accumulatedDepth < cycle.depth) && + (cycle.incrementalDepthReduction > 0))) { + expandCyclePoint(x, y, z); + } else if (cycle.accumulatedDepth < cycle.depth) { + writeBlock( + gRetractModal.format(98), gCycleModal.format(73), + getCommonCycle(x, y, z, cycle.retract, cycle.clearance), + ("Q" + xyzFormat.format(cycle.incrementalDepth)), + ("K" + xyzFormat.format(cycle.accumulatedDepth)), + conditional(P > 0, "P" + milliFormat.format(P)), // optional + feedOutput.format(F) + ); + } else { + writeBlock( + gRetractModal.format(98), gCycleModal.format(73), + getCommonCycle(x, y, z, cycle.retract, cycle.clearance), + (((cycle.incrementalDepthReduction > 0) ? "I" : "Q") + xyzFormat.format(cycle.incrementalDepth)), + conditional(cycle.incrementalDepthReduction > 0, "J" + xyzFormat.format(cycle.incrementalDepthReduction)), + conditional(cycle.incrementalDepthReduction > 0, "K" + xyzFormat.format(cycle.minimumIncrementalDepth)), + conditional(P > 0, "P" + milliFormat.format(P)), // optional + feedOutput.format(F) + ); + } + break; + case "deep-drilling": + writeBlock( + gRetractModal.format(98), gCycleModal.format(83), + getCommonCycle(x, y, z, cycle.retract, cycle.clearance), + (((cycle.incrementalDepthReduction > 0) ? "I" : "Q") + xyzFormat.format(cycle.incrementalDepth)), + conditional(cycle.incrementalDepthReduction > 0, "J" + xyzFormat.format(cycle.incrementalDepthReduction)), + conditional(cycle.incrementalDepthReduction > 0, "K" + xyzFormat.format(cycle.minimumIncrementalDepth)), + conditional(P > 0, "P" + milliFormat.format(P)), // optional + feedOutput.format(F) + ); + break; + case "tapping": + var tappingFPM = tool.getThreadPitch() * rpmFormat.getResultingValue(spindleSpeed); + F = (properties.useG95forTapping ? tool.getThreadPitch() : tappingFPM); + if (properties.useG95forTapping) { + writeBlock(gFeedModeModal.format(95)); + } + writeBlock( + gRetractModal.format(98), gCycleModal.format((tool.type == TOOL_TAP_LEFT_HAND) ? 74 : 84), + getCommonCycle(x, y, z, cycle.retract, cycle.clearance), + pitchOutput.format(F) + ); + forceFeed(); + break; + case "left-tapping": + var tappingFPM = tool.getThreadPitch() * rpmFormat.getResultingValue(spindleSpeed); + F = (properties.useG95forTapping ? tool.getThreadPitch() : tappingFPM); + if (properties.useG95forTapping) { + writeBlock(gFeedModeModal.format(95)); + } + writeBlock( + gRetractModal.format(98), gCycleModal.format(74), + getCommonCycle(x, y, z, cycle.retract, cycle.clearance), + pitchOutput.format(F) + ); + forceFeed(); + break; + case "right-tapping": + var tappingFPM = tool.getThreadPitch() * rpmFormat.getResultingValue(spindleSpeed); + F = (properties.useG95forTapping ? tool.getThreadPitch() : tappingFPM); + if (properties.useG95forTapping) { + writeBlock(gFeedModeModal.format(95)); + } + writeBlock( + gRetractModal.format(98), gCycleModal.format(84), + getCommonCycle(x, y, z, cycle.retract, cycle.clearance), + pitchOutput.format(F) + ); + forceFeed(); + break; + case "tapping-with-chip-breaking": + case "left-tapping-with-chip-breaking": + case "right-tapping-with-chip-breaking": + var tappingFPM = tool.getThreadPitch() * rpmFormat.getResultingValue(spindleSpeed); + F = (properties.useG95forTapping ? tool.getThreadPitch() : tappingFPM); + if (properties.useG95forTapping) { + writeBlock(gFeedModeModal.format(95)); + } + // Parameter 57 bit 6, REPT RIG TAP, is set to 1 (On) + // On Mill software versions12.09 and above, REPT RIG TAP has been moved from the Parameters to Setting 133 + var u = cycle.stock; + var step = cycle.incrementalDepth; + var first = true; + while (u > cycle.bottom) { + if (step < cycle.minimumIncrementalDepth) { + step = cycle.minimumIncrementalDepth; + } + u -= step; + step -= cycle.incrementalDepthReduction; + gCycleModal.reset(); // required + if ((u - 0.001) <= cycle.bottom) { + u = cycle.bottom; + } + if (first) { + first = false; + writeBlock( + gRetractModal.format(99), gCycleModal.format((tool.type == TOOL_TAP_LEFT_HAND ? 74 : 84)), + getCommonCycle((gPlaneModal.getCurrent() == 19) ? u : x, (gPlaneModal.getCurrent() == 18) ? u : y, (gPlaneModal.getCurrent() == 17) ? u : z, cycle.retract, cycle.clearance), + pitchOutput.format(F) + ); + } else { + var position; + var depth; + switch (gPlaneModal.getCurrent()) { + case 17: + xOutput.reset(); + position = xOutput.format(x); + depth = "Z" + xyzFormat.format(u); + break; + case 18: + zOutput.reset(); + position = zOutput.format(z); + depth = "Y" + xyzFormat.format(u); + break; + case 19: + yOutput.reset(); + position = yOutput.format(y); + depth = "X" + xyzFormat.format(u); + break; + } + writeBlock(conditional(u <= cycle.bottom, gRetractModal.format(98)), position, depth); + } + } + forceFeed(); + break; + case "fine-boring": + writeBlock( + gRetractModal.format(98), gCycleModal.format(76), + getCommonCycle(x, y, z, cycle.retract, cycle.clearance), + "P" + milliFormat.format(P), // not optional + "Q" + xyzFormat.format(cycle.shift), + feedOutput.format(F) + ); + forceSpindleSpeed = true; + break; + case "back-boring": + if (P > 0) { + expandCyclePoint(x, y, z); + } else { + 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), gCycleModal.format(77), + getCommonCycle(x - dx, y - dy, z - dz, cycle.bottom, cycle.clearance), + "Q" + xyzFormat.format(cycle.shift), + feedOutput.format(F) + ); + forceSpindleSpeed = true; + } + break; + case "reaming": + writeBlock( + gRetractModal.format(98), gCycleModal.format(85), + getCommonCycle(x, y, z, cycle.retract, cycle.clearance), + feedOutput.format(F) + ); + break; + case "stop-boring": + if (P > 0) { + expandCyclePoint(x, y, z); + } else { + writeBlock( + gRetractModal.format(98), gCycleModal.format(86), + getCommonCycle(x, y, z, cycle.retract, cycle.clearance), + feedOutput.format(F) + ); + forceSpindleSpeed = true; + } + break; + case "manual-boring": + writeBlock( + gRetractModal.format(98), gCycleModal.format(88), + getCommonCycle(x, y, z, cycle.retract, cycle.clearance), + "P" + milliFormat.format(P), // not optional + feedOutput.format(F) + ); + break; + case "boring": + writeBlock( + gRetractModal.format(98), gCycleModal.format(89), + getCommonCycle(x, y, z, cycle.retract, cycle.clearance), + "P" + milliFormat.format(P), // not optional + feedOutput.format(F) + ); + break; + + case "probing-x": + protectedProbeMove(cycle, x, y, z - cycle.depth); + writeBlock( + gFormat.format(65), "P" + 9811, + "X" + xyzFormat.format(x + approach(cycle.approach1) * (cycle.probeClearance + tool.diameter / 2)), + "Q" + xyzFormat.format(cycle.probeOvertravel), + getProbingArguments(cycle, probeWorkOffsetCode) + ); + break; + case "probing-y": + protectedProbeMove(cycle, x, y, z - cycle.depth); + writeBlock( + gFormat.format(65), "P" + 9811, + "Y" + xyzFormat.format(y + approach(cycle.approach1) * (cycle.probeClearance + tool.diameter / 2)), + "Q" + xyzFormat.format(cycle.probeOvertravel), + getProbingArguments(cycle, probeWorkOffsetCode) + ); + break; + case "probing-z": + protectedProbeMove(cycle, x, y, Math.min(z - cycle.depth + cycle.probeClearance, cycle.retract)); + writeBlock( + gFormat.format(65), "P" + 9811, + "Z" + xyzFormat.format(z - cycle.depth), + "Q" + xyzFormat.format(cycle.probeOvertravel), + getProbingArguments(cycle, probeWorkOffsetCode) + ); + break; + case "probing-x-wall": + protectedProbeMove(cycle, x, y, z); + writeBlock( + gFormat.format(65), "P" + 9812, + "X" + xyzFormat.format(cycle.width1), + zOutput.format(z - cycle.depth), + "Q" + xyzFormat.format(cycle.probeOvertravel), + "R" + xyzFormat.format(cycle.probeClearance), + getProbingArguments(cycle, probeWorkOffsetCode) + ); + break; + case "probing-y-wall": + protectedProbeMove(cycle, x, y, z); + writeBlock( + gFormat.format(65), "P" + 9812, + "Y" + xyzFormat.format(cycle.width1), + zOutput.format(z - cycle.depth), + "Q" + xyzFormat.format(cycle.probeOvertravel), + "R" + xyzFormat.format(cycle.probeClearance), + getProbingArguments(cycle, probeWorkOffsetCode) + ); + break; + case "probing-x-channel": + protectedProbeMove(cycle, x, y, z - cycle.depth); + writeBlock( + gFormat.format(65), "P" + 9812, + "X" + xyzFormat.format(cycle.width1), + "Q" + xyzFormat.format(cycle.probeOvertravel), + // not required "R" + xyzFormat.format(cycle.probeClearance), + getProbingArguments(cycle, probeWorkOffsetCode) + ); + break; + case "probing-x-channel-with-island": + protectedProbeMove(cycle, x, y, z); + writeBlock( + gFormat.format(65), "P" + 9812, + "X" + xyzFormat.format(cycle.width1), + zOutput.format(z - cycle.depth), + "Q" + xyzFormat.format(cycle.probeOvertravel), + "R" + xyzFormat.format(-cycle.probeClearance), + getProbingArguments(cycle, probeWorkOffsetCode) + ); + break; + case "probing-y-channel": + protectedProbeMove(cycle, x, y, z - cycle.depth); + writeBlock( + gFormat.format(65), "P" + 9812, + "Y" + xyzFormat.format(cycle.width1), + "Q" + xyzFormat.format(cycle.probeOvertravel), + // not required "R" + xyzFormat.format(cycle.probeClearance), + getProbingArguments(cycle, probeWorkOffsetCode) + ); + break; + case "probing-y-channel-with-island": + protectedProbeMove(cycle, x, y, z); + writeBlock( + gFormat.format(65), "P" + 9812, + "Y" + xyzFormat.format(cycle.width1), + zOutput.format(z - cycle.depth), + "Q" + xyzFormat.format(cycle.probeOvertravel), + "R" + xyzFormat.format(-cycle.probeClearance), + getProbingArguments(cycle, probeWorkOffsetCode) + ); + break; + case "probing-xy-circular-boss": + protectedProbeMove(cycle, x, y, z); + writeBlock( + gFormat.format(65), "P" + 9814, + "D" + xyzFormat.format(cycle.width1), + "Z" + xyzFormat.format(z - cycle.depth), + "Q" + xyzFormat.format(cycle.probeOvertravel), + "R" + xyzFormat.format(cycle.probeClearance), + getProbingArguments(cycle, probeWorkOffsetCode) + ); + break; + case "probing-xy-circular-partial-boss": + protectedProbeMove(cycle, x, y, z); + writeBlock( + gFormat.format(65), "P" + 9823, + "A" + xyzFormat.format(cycle.partialCircleAngleA), + "B" + xyzFormat.format(cycle.partialCircleAngleB), + "C" + xyzFormat.format(cycle.partialCircleAngleC), + "D" + xyzFormat.format(cycle.width1), + "Z" + xyzFormat.format(z - cycle.depth), + "Q" + xyzFormat.format(cycle.probeOvertravel), + "R" + xyzFormat.format(cycle.probeClearance), + getProbingArguments(cycle, probeWorkOffsetCode) + ); + break; + case "probing-xy-circular-hole": + protectedProbeMove(cycle, x, y, z - cycle.depth); + writeBlock( + gFormat.format(65), "P" + 9814, + "D" + xyzFormat.format(cycle.width1), + "Q" + xyzFormat.format(cycle.probeOvertravel), + // not required "R" + xyzFormat.format(cycle.probeClearance), + getProbingArguments(cycle, probeWorkOffsetCode) + ); + break; + case "probing-xy-circular-partial-hole": + protectedProbeMove(cycle, x, y, z - cycle.depth); + writeBlock( + gFormat.format(65), "P" + 9823, + "A" + xyzFormat.format(cycle.partialCircleAngleA), + "B" + xyzFormat.format(cycle.partialCircleAngleB), + "C" + xyzFormat.format(cycle.partialCircleAngleC), + "D" + xyzFormat.format(cycle.width1), + "Q" + xyzFormat.format(cycle.probeOvertravel), + getProbingArguments(cycle, probeWorkOffsetCode) + ); + break; + case "probing-xy-circular-hole-with-island": + protectedProbeMove(cycle, x, y, z); + writeBlock( + gFormat.format(65), "P" + 9814, + "Z" + xyzFormat.format(z - cycle.depth), + "D" + xyzFormat.format(cycle.width1), + "Q" + xyzFormat.format(cycle.probeOvertravel), + "R" + xyzFormat.format(-cycle.probeClearance), + getProbingArguments(cycle, probeWorkOffsetCode) + ); + break; + case "probing-xy-circular-partial-hole-with-island": + protectedProbeMove(cycle, x, y, z); + writeBlock( + gFormat.format(65), "P" + 9823, + "Z" + xyzFormat.format(z - cycle.depth), + "A" + xyzFormat.format(cycle.partialCircleAngleA), + "B" + xyzFormat.format(cycle.partialCircleAngleB), + "C" + xyzFormat.format(cycle.partialCircleAngleC), + "D" + xyzFormat.format(cycle.width1), + "Q" + xyzFormat.format(cycle.probeOvertravel), + "R" + xyzFormat.format(-cycle.probeClearance), + getProbingArguments(cycle, probeWorkOffsetCode) + ); + break; + case "probing-xy-rectangular-hole": + protectedProbeMove(cycle, x, y, z - cycle.depth); + writeBlock( + gFormat.format(65), "P" + 9812, + "X" + xyzFormat.format(cycle.width1), + "Q" + xyzFormat.format(cycle.probeOvertravel), + // not required "R" + xyzFormat.format(-cycle.probeClearance), + getProbingArguments(cycle, probeWorkOffsetCode) + ); + writeBlock( + gFormat.format(65), "P" + 9812, + "Y" + xyzFormat.format(cycle.width2), + "Q" + xyzFormat.format(cycle.probeOvertravel), + // not required "R" + xyzFormat.format(-cycle.probeClearance), + getProbingArguments(cycle, probeWorkOffsetCode) + ); + break; + case "probing-xy-rectangular-boss": + protectedProbeMove(cycle, x, y, z); + writeBlock( + gFormat.format(65), "P" + 9812, + "Z" + xyzFormat.format(z - cycle.depth), + "X" + xyzFormat.format(cycle.width1), + "R" + xyzFormat.format(cycle.probeClearance), + "Q" + xyzFormat.format(cycle.probeOvertravel), + getProbingArguments(cycle, probeWorkOffsetCode) + ); + writeBlock( + gFormat.format(65), "P" + 9812, + "Z" + xyzFormat.format(z - cycle.depth), + "Y" + xyzFormat.format(cycle.width2), + "R" + xyzFormat.format(cycle.probeClearance), + "Q" + xyzFormat.format(cycle.probeOvertravel), + getProbingArguments(cycle, probeWorkOffsetCode) + ); + break; + case "probing-xy-rectangular-hole-with-island": + protectedProbeMove(cycle, x, y, z); + writeBlock( + gFormat.format(65), "P" + 9812, + "Z" + xyzFormat.format(z - cycle.depth), + "X" + xyzFormat.format(cycle.width1), + "Q" + xyzFormat.format(cycle.probeOvertravel), + "R" + xyzFormat.format(-cycle.probeClearance), + getProbingArguments(cycle, probeWorkOffsetCode) + ); + writeBlock( + gFormat.format(65), "P" + 9812, + "Z" + xyzFormat.format(z - cycle.depth), + "Y" + xyzFormat.format(cycle.width2), + "Q" + xyzFormat.format(cycle.probeOvertravel), + "R" + xyzFormat.format(-cycle.probeClearance), + getProbingArguments(cycle, probeWorkOffsetCode) + ); + break; + + case "probing-xy-inner-corner": + var cornerX = x + approach(cycle.approach1) * (cycle.probeClearance + tool.diameter / 2); + var cornerY = y + approach(cycle.approach2) * (cycle.probeClearance + tool.diameter / 2); + var cornerI = 0; + var cornerJ = 0; + if (cycle.probeSpacing && (cycle.probeSpacing != 0)) { + cornerI = cycle.probeSpacing; + cornerJ = cycle.probeSpacing; + } + if ((cornerI != 0) && (cornerJ != 0)) { + g68RotationMode = 2; + } + protectedProbeMove(cycle, x, y, z - cycle.depth); + writeBlock( + gFormat.format(65), "P" + 9815, xOutput.format(cornerX), yOutput.format(cornerY), + conditional(cornerI != 0, "I" + xyzFormat.format(cornerI)), + conditional(cornerJ != 0, "J" + xyzFormat.format(cornerJ)), + "Q" + xyzFormat.format(cycle.probeOvertravel), + conditional((g68RotationMode == 0) || (angularProbingMode == ANGLE_PROBE_USE_CAXIS), getProbingArguments(cycle, probeWorkOffsetCode)) + ); + break; + case "probing-xy-outer-corner": + var cornerX = x + approach(cycle.approach1) * (cycle.probeClearance + tool.diameter / 2); + var cornerY = y + approach(cycle.approach2) * (cycle.probeClearance + tool.diameter / 2); + var cornerI = 0; + var cornerJ = 0; + if (cycle.probeSpacing && (cycle.probeSpacing != 0)) { + cornerI = cycle.probeSpacing; + cornerJ = cycle.probeSpacing; + } + if ((cornerI != 0) && (cornerJ != 0)) { + g68RotationMode = 2; + } + protectedProbeMove(cycle, x, y, z - cycle.depth); + writeBlock( + gFormat.format(65), "P" + 9816, xOutput.format(cornerX), yOutput.format(cornerY), + conditional(cornerI != 0, "I" + xyzFormat.format(cornerI)), + conditional(cornerJ != 0, "J" + xyzFormat.format(cornerJ)), + "Q" + xyzFormat.format(cycle.probeOvertravel), + conditional((g68RotationMode == 0) || (angularProbingMode == ANGLE_PROBE_USE_CAXIS), getProbingArguments(cycle, probeWorkOffsetCode)) + ); + break; + case "probing-x-plane-angle": + protectedProbeMove(cycle, x, y, z - cycle.depth); + writeBlock( + gFormat.format(65), "P" + 9843, + "X" + xyzFormat.format(x + approach(cycle.approach1) * (cycle.probeClearance + tool.diameter / 2)), + "D" + xyzFormat.format(cycle.probeSpacing), + "Q" + xyzFormat.format(cycle.probeOvertravel), + "A" + xyzFormat.format(cycle.nominalAngle != undefined ? cycle.nominalAngle : 90), + getProbingArguments(cycle, false) + ); + g68RotationMode = 1; + break; + case "probing-y-plane-angle": + protectedProbeMove(cycle, x, y, z - cycle.depth); + writeBlock( + gFormat.format(65), "P" + 9843, + "Y" + xyzFormat.format(y + approach(cycle.approach1) * (cycle.probeClearance + tool.diameter / 2)), + "D" + xyzFormat.format(cycle.probeSpacing), + "Q" + xyzFormat.format(cycle.probeOvertravel), + "A" + xyzFormat.format(cycle.nominalAngle != undefined ? cycle.nominalAngle : 0), + getProbingArguments(cycle, false) + ); + g68RotationMode = 1; + break; + case "probing-xy-pcd-hole": + protectedProbeMove(cycle, x, y, z); + writeBlock( + gFormat.format(65), "P" + 9819, + "A" + xyzFormat.format(cycle.pcdStartingAngle), + "B" + xyzFormat.format(cycle.numberOfSubfeatures), + "C" + xyzFormat.format(cycle.widthPCD), + "D" + xyzFormat.format(cycle.widthFeature), + "K" + xyzFormat.format(z - cycle.depth), + "Q" + xyzFormat.format(cycle.probeOvertravel), + getProbingArguments(cycle, false) + ); + if (cycle.updateToolWear) { + error(localize("Update tool action is not supported with this cycle")); + return; + } + break; + case "probing-xy-pcd-boss": + protectedProbeMove(cycle, x, y, z); + writeBlock( + gFormat.format(65), "P" + 9819, + "A" + xyzFormat.format(cycle.pcdStartingAngle), + "B" + xyzFormat.format(cycle.numberOfSubfeatures), + "C" + xyzFormat.format(cycle.widthPCD), + "D" + xyzFormat.format(cycle.widthFeature), + "Z" + xyzFormat.format(z - cycle.depth), + "Q" + xyzFormat.format(cycle.probeOvertravel), + "R" + xyzFormat.format(cycle.probeClearance), + getProbingArguments(cycle, false) + ); + if (cycle.updateToolWear) { + error(localize("Update tool action is not supported with this cycle")); + return; + } + break; + default: + expandCyclePoint(x, y, z); + } + + // place cycle operation in subprogram + if (cycleSubprogramIsActive) { + if (forceCycle || cycleExpanded || isProbeOperation()) { + cycleSubprogramIsActive = false; + } else { + // call subprogram + writeBlock(mFormat.format(97), "P" + nFormat.format(currentSubprogram)); + subprogramStart(new Vector(x, y, z), new Vector(0, 0, 0), false); + } + } + if (incrementalMode) { // set current position to clearance height + setCyclePosition(cycle.clearance); + } + + // 2nd through nth cycle point + } else { + if (cycleExpanded) { + expandCyclePoint(x, y, z); + } else { + var _x; + var _y; + var _z; + if (!xyzFormat.areDifferent(x, xOutput.getCurrent()) && + !xyzFormat.areDifferent(y, yOutput.getCurrent()) && + !xyzFormat.areDifferent(z, zOutput.getCurrent())) { + switch (gPlaneModal.getCurrent()) { + case 17: // XY + xOutput.reset(); // at least one axis is required + break; + case 18: // ZX + zOutput.reset(); // at least one axis is required + break; + case 19: // YZ + yOutput.reset(); // at least one axis is required + break; + } + } + if (incrementalMode) { // set current position to retract height + setCyclePosition(cycle.retract); + } + writeBlock(xOutput.format(x), yOutput.format(y), zOutput.format(z)); + if (incrementalMode) { // set current position to clearance height + setCyclePosition(cycle.clearance); + } + } + } +} + +function getProbingArguments(cycle, probeWorkOffsetCode) { + var probeWCS = hasParameter("operation-strategy") && (getParameter("operation-strategy") == "probe"); + return [ + (cycle.angleAskewAction == "stop-message" ? "B" + xyzFormat.format(cycle.toleranceAngle ? cycle.toleranceAngle : 0) : undefined), + ((cycle.updateToolWear && cycle.toolWearErrorCorrection < 100) ? "F" + xyzFormat.format(cycle.toolWearErrorCorrection ? cycle.toolWearErrorCorrection / 100 : 100) : undefined), + (cycle.wrongSizeAction == "stop-message" ? "H" + xyzFormat.format(cycle.toleranceSize ? cycle.toleranceSize : 0) : undefined), + (cycle.outOfPositionAction == "stop-message" ? "M" + xyzFormat.format(cycle.tolerancePosition ? cycle.tolerancePosition : 0) : undefined), + ((cycle.updateToolWear && cycleType == "probing-z") ? "T" + xyzFormat.format(cycle.toolLengthOffset) : undefined), + ((cycle.updateToolWear && cycleType !== "probing-z") ? "T" + xyzFormat.format(cycle.toolDiameterOffset) : undefined), + (cycle.updateToolWear ? "V" + xyzFormat.format(cycle.toolWearUpdateThreshold ? cycle.toolWearUpdateThreshold : 0) : undefined), + (cycle.printResults ? "W" + xyzFormat.format(1 + cycle.incrementComponent) : undefined), // 1 for advance feature, 2 for reset feature count and advance component number. first reported result in a program should use W2. + conditional(probeWorkOffsetCode && probeWCS, "S" + probeWorkOffsetCode) + ]; +} + +function onCycleEnd() { + if (isProbeOperation()) { + zOutput.reset(); + gMotionModal.reset(); + writeBlock(gFormat.format(65), "P" + 9810, zOutput.format(cycle.retract)); // protected retract move + } else { + if (cycleSubprogramIsActive) { + subprogramEnd(); + cycleSubprogramIsActive = false; + } + if (!cycleExpanded) { + writeBlock(gCycleModal.format(80), conditional(properties.useG95forTapping, gFeedModeModal.format(94))); + 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; + } + if (!properties.useG0 && (((x ? 1 : 0) + (y ? 1 : 0) + (z ? 1 : 0)) > 1)) { + // axes are not synchronized + writeBlock(gFeedModeModal.format(94), gMotionModal.format(1), x, y, z, getFeed(highFeedrate)); + } else { + writeBlock(gMotionModal.format(0), x, y, z); + forceFeed(); + } + } +} + +function onLinear(_x, _y, _z, feed) { + if (pendingRadiusCompensation >= 0) { + // ensure that we end at desired position when compensation is turned off + xOutput.reset(); + yOutput.reset(); + } + var x = xOutput.format(_x); + var y = yOutput.format(_y); + var z = zOutput.format(_z); + var f = getFeed(feed); + if (x || y || z) { + if (pendingRadiusCompensation >= 0) { + pendingRadiusCompensation = -1; + var d = tool.diameterOffset; + if ((d > 200 && d < 1000) || d > 9999) { + warning(localize("Diameter offset out of range.")); + } + writeBlock(gPlaneModal.format(17)); + switch (radiusCompensation) { + case RADIUS_COMPENSATION_LEFT: + dOutput.reset(); + writeBlock(gFeedModeModal.format(94), gMotionModal.format(1), gFormat.format(41), x, y, z, dOutput.format(d), f); + break; + case RADIUS_COMPENSATION_RIGHT: + dOutput.reset(); + writeBlock(gFeedModeModal.format(94), gMotionModal.format(1), gFormat.format(42), x, y, z, dOutput.format(d), f); + break; + default: + writeBlock(gFeedModeModal.format(94), gMotionModal.format(1), gFormat.format(40), 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; + } + if (!properties.useG0) { + // axes are not synchronized + onLinear5D(_x, _y, _z, _a, _b, _c, highFeedrate); + } else { + 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(0), x, y, z, a, b, c); + 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 (properties.useDPMFeeds) { + f.frn = feedOutput.format(f.frn); + } else { + f.frn = inverseTimeOutput.format(f.frn); + } + } else { + f.frn = getFeed(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 'properties.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 = 1.0; // ratio of rotary accuracy to linear accuracy for DPM calculations +var inverseTimeUnits = 1.0; // 1.0 = minutes, 60.0 = seconds +var maxInverseTime = 45000; // 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:(unit == MM ? 2 : 3), 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 (!properties.useDPMFeeds) { // 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; // !getOptimizedMode() && (forceOptimized == undefined); // set to false for rotary heads + 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 (false) { // 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 + var feedRate = _feed / (_moveLength.radius.x / (toPreciseUnit(properties.setting34, IN) / 2.0)); + dpmFeed = Math.min(feedRate, highFeedrate); + if (Math.abs(dpmFeed - previousDPMFeed) < dpmFeedToler) { + dpmFeed = previousDPMFeed; + } + } + 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 + +function onCircular(clockwise, cx, cy, cz, x, y, z, feed) { + if (isSpiral()) { + var startRadius = getCircularStartRadius(); + var endRadius = getCircularRadius(); + var dr = Math.abs(endRadius - startRadius); + if (dr > maximumCircularRadiiDifference) { // maximum limit + linearize(tolerance); // or alternatively use other G-codes for spiral motion + return; + } + } + + if (pendingRadiusCompensation >= 0) { + error(localize("Radius compensation cannot be activated/deactivated for a circular move.")); + return; + } + + var start = getCurrentPosition(); + + if (isFullCircle()) { + if (properties.useRadius || isHelical()) { // radius mode does not support full arcs + linearize(tolerance); + return; + } + switch (getCircularPlane()) { + case PLANE_XY: + writeBlock(gPlaneModal.format(17), gFeedModeModal.format(94), gMotionModal.format(clockwise ? 2 : 3), iOutput.format(cx - start.x, 0), jOutput.format(cy - start.y, 0), getFeed(feed)); + break; + case PLANE_ZX: + writeBlock(gPlaneModal.format(18), gFeedModeModal.format(94), gMotionModal.format(clockwise ? 2 : 3), iOutput.format(cx - start.x, 0), kOutput.format(cz - start.z, 0), getFeed(feed)); + break; + case PLANE_YZ: + writeBlock(gPlaneModal.format(19), gFeedModeModal.format(94), 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 (!properties.useRadius) { + switch (getCircularPlane()) { + case PLANE_XY: + writeBlock(gPlaneModal.format(17), gFeedModeModal.format(94), 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(gPlaneModal.format(18), gFeedModeModal.format(94), 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(gPlaneModal.format(19), gFeedModeModal.format(94), 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(gPlaneModal.format(17), gFeedModeModal.format(94), 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(gPlaneModal.format(18), gFeedModeModal.format(94), 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(gPlaneModal.format(19), gFeedModeModal.format(94), 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; +var isOptionalCoolant = false; + +function setCoolant(coolant) { + var coolantCodes = getCoolantCodes(coolant); + if (Array.isArray(coolantCodes)) { + if (singleLineCoolant) { + skipBlock = isOptionalCoolant; + writeBlock(coolantCodes.join(getWordSeparator())); + } else { + for (var c in coolantCodes) { + skipBlock = isOptionalCoolant; + writeBlock(coolantCodes[c]); + } + } + return undefined; + } + return coolantCodes; +} + +var isSpecialCoolantActive = false; + +function getCoolantCodes(coolant) { + isOptionalCoolant = false; + 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) { + if (operationNeedsSafeStart && coolant != COOLANT_OFF && !isSpecialCoolantActive) { + isOptionalCoolant = true; + } else { + return undefined; // coolant is already active + } + } + if ((coolant != COOLANT_OFF) && (currentCoolantMode != COOLANT_OFF) && !isOptionalCoolant) { + if (Array.isArray(coolantOff)) { + for (var i in coolantOff) { + multipleCoolantBlocks.push(mFormat.format(coolantOff[i])); + } + } else { + multipleCoolantBlocks.push(mFormat.format(coolantOff)); + } + } + + if (isSpecialCoolantActive) { + forceSpindleSpeed = true; + } + var m; + var coolantCodes = {}; + for (var c in coolants) { // find required coolant codes into the coolants array + if (coolants[c].id == coolant) { + isSpecialCoolantActive = (coolants[c].id == COOLANT_THROUGH_TOOL) || (coolants[c].id == COOLANT_FLOOD_THROUGH_TOOL) || (coolants[c].id == COOLANT_AIR_THROUGH_TOOL); + 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_COOLANT_ON: + setCoolant(COOLANT_FLOOD); + return; + case COMMAND_COOLANT_OFF: + setCoolant(COOLANT_OFF); + return; + case COMMAND_START_SPINDLE: + onCommand(tool.clockwise ? COMMAND_SPINDLE_CLOCKWISE : COMMAND_SPINDLE_COUNTERCLOCKWISE); + return; + case COMMAND_LOCK_MULTI_AXIS: + if (machineConfiguration.isMultiAxisConfiguration() && (machineConfiguration.getNumberOfAxes() >= 4)) { + var _skipBlock = skipBlock; + writeBlock(mFormat.format(10)); // lock 4th-axis motion + if (machineConfiguration.getNumberOfAxes() == 5) { + skipBlock = _skipBlock; + writeBlock(mFormat.format(12)); // lock 5th-axis motion + } + } + return; + case COMMAND_UNLOCK_MULTI_AXIS: + if (machineConfiguration.isMultiAxisConfiguration() && (machineConfiguration.getNumberOfAxes() >= 4)) { + var _skipBlock = skipBlock; + writeBlock(mFormat.format(11)); // unlock 4th-axis motion + if (machineConfiguration.getNumberOfAxes() == 5) { + skipBlock = _skipBlock; + writeBlock(mFormat.format(13)); // unlock 5th-axis motion + } + } + return; + case COMMAND_BREAK_CONTROL: + if (!toolChecked) { // avoid duplicate COMMAND_BREAK_CONTROL + onCommand(COMMAND_STOP_SPINDLE); + onCommand(COMMAND_COOLANT_OFF); + writeBlock( + gFormat.format(65), + "P" + 9853, + "T" + toolFormat.format(tool.number), + "B" + xyzFormat.format(0), + "H" + xyzFormat.format(properties.toolBreakageTolerance) + ); + toolChecked = true; + } + return; + case COMMAND_TOOL_MEASURE: + measureTool = true; + return; + case COMMAND_START_CHIP_TRANSPORT: + writeBlock(mFormat.format(31)); + return; + case COMMAND_STOP_CHIP_TRANSPORT: + writeBlock(mFormat.format(33)); + return; + case COMMAND_PROBE_ON: + return; + case COMMAND_PROBE_OFF: + return; + } + + var stringId = getCommandStringId(command); + var mcode = mapCommand[stringId]; + if (mcode != undefined) { + writeBlock(mFormat.format(mcode)); + } else { + onUnsupportedCommand(command); + } +} + +var toolChecked = false; // specifies that the tool has been checked with the probe + +function onSectionEnd() { + if (currentSection.isMultiAxis()) { + if (useInverseTimeFeed) { + writeBlock(gFeedModeModal.format(94)); // inverse time feed off + } + } + if (typeof inspectionProcessSectionEnd == "function") { + inspectionProcessSectionEnd(); + } + if (!isLastSection() && (getNextSection().getTool().coolant != tool.coolant)) { + setCoolant(COOLANT_OFF); + } + if ((((getCurrentSectionId() + 1) >= getNumberOfSections()) || + (tool.number != getNextSection().getTool().number)) && + tool.breakControl) { + onCommand(COMMAND_BREAK_CONTROL); + } else { + toolChecked = false; + } + + if (true) { + if (isRedirecting()) { + if (firstPattern) { + var finalPosition = getFramePosition(currentSection.getFinalPosition()); + var abc; + if (currentSection.isMultiAxis() && machineConfiguration.isMultiAxisConfiguration()) { + abc = currentSection.getFinalToolAxisABC(); + } else { + abc = currentWorkPlaneABC; + } + if (abc == undefined) { + abc = new Vector(0, 0, 0); + } + setAbsoluteMode(finalPosition, abc); + subprogramEnd(); + } + } + } + + // 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(); + + if (isProbeOperation()) { + writeBlock(gFormat.format(65), "P" + 9833); // spin the probe off + if (angularProbingMode == ANGLE_PROBE_USE_CAXIS) { + setProbingAngle(); // define rotation of part + } + } + + operationNeedsSafeStart = false; // reset for next section +} + +/** Output block to do safe retract and/or move to home position. */ +function writeRetract() { + // initialize routine + var _xyzMoved = new Array(false, false, false); + var _useG28 = properties.useG28; // can be either true or false + + // check syntax of call + if (arguments.length == 0) { + error(localize("No axis specified for writeRetract().")); + return; + } + for (var i = 0; i < arguments.length; ++i) { + if ((arguments[i] < 0) || (arguments[i] > 2)) { + error(localize("Bad axis specified for writeRetract().")); + return; + } + if (_xyzMoved[arguments[i]]) { + error(localize("Cannot retract the same axis twice in one line")); + return; + } + _xyzMoved[arguments[i]] = true; + } + + // special conditions + if (_useG28 && _xyzMoved[2] && (_xyzMoved[0] || _xyzMoved[1])) { // XY don't use G28 + error(localize("You cannot move home in XY & Z in the same block.")); + return; + } + if (_xyzMoved[0] || _xyzMoved[1]) { + _useG28 = false; + } + + // define home positions + var _xHome; + var _yHome; + var _zHome; + if (_useG28) { + _xHome = 0; + _yHome = 0; + _zHome = 0; + } else { + if (properties.homePositionCenter && + hasParameter("part-upper-x") && hasParameter("part-lower-x")) { + _xHome = (getParameter("part-upper-x") + getParameter("part-lower-x")) / 2; + } else { + _xHome = machineConfiguration.hasHomePositionX() ? machineConfiguration.getHomePositionX() : 0; + } + _yHome = machineConfiguration.hasHomePositionY() ? machineConfiguration.getHomePositionY() : 0; + _zHome = 3.6; + } + + // format home positions + var words = []; // store all retracted axes in an array + for (var i = 0; i < arguments.length; ++i) { + // define the axes to move + switch (arguments[i]) { + case X: + // special conditions + if (properties.homePositionCenter) { // output X in standard block by itself if centering + writeBlock(gMotionModal.format(0), xOutput.format(_xHome)); + _xyzMoved[0] = false; + break; + } + words.push("X" + xyzFormat.format(_xHome)); + break; + case Y: + words.push("Y" + xyzFormat.format(_yHome)); + break; + case Z: + words.push("Z" + xyzFormat.format(_zHome)); + retracted = !skipBlock; + break; + } + } + + // output move to home + if (words.length > 0) { + if (_useG28) { + gAbsIncModal.reset(); + writeBlock(gFormat.format(28), gAbsIncModal.format(91), words); + writeBlock(gAbsIncModal.format(90)); + } else { + gMotionModal.reset(); + writeBlock(gAbsIncModal.format(90), gFormat.format(53), gMotionModal.format(0), words); + } + + // force any axes that move to home on next block + if (_xyzMoved[0]) { + xOutput.reset(); + } + if (_xyzMoved[1]) { + yOutput.reset(); + } + if (_xyzMoved[2]) { + zOutput.reset(); + } + } +} + +function onClose() { + nullProbeAngle(false); + writeln(""); + + optionalSection = false; + + onCommand(COMMAND_STOP_SPINDLE); + onCommand(COMMAND_COOLANT_OFF); + + // retract + writeRetract(Z); + zOutput.reset(); + + forceWorkPlane(); + setWorkPlane(new Vector(0, 0, 0)); // reset working plane + writeBlock(gRotationModal.format(69)); + + writeRetract(X, Y); + + onImpliedCommand(COMMAND_END); + onImpliedCommand(COMMAND_STOP_SPINDLE); + writeBlock(mFormat.format(30)); // stop program, spindle stop, coolant off + if (subprograms.length > 0) { + writeln(""); + write(subprograms); + } + writeln(""); + writeln("%"); +} + +/* +keywords += (keywords ? " MODEL_IMAGE" : "MODEL_IMAGE"); + +function onTerminate() { + var outputPath = getOutputPath(); + var programFilename = FileSystem.getFilename(outputPath); + var programSize = FileSystem.getFileSize(outputPath); + var postPath = findFile("setup-sheet-excel-2007.cps"); + var intermediatePath = getIntermediatePath(); + var a = "--property unit " + ((unit == IN) ? "0" : "1"); // use 0 for inch and 1 for mm + if (programName) { + a += " --property programName \"'" + programName + "'\""; + } + if (programComment) { + a += " --property programComment \"'" + programComment + "'\""; + } + a += " --property programFilename \"'" + programFilename + "'\""; + a += " --property programSize \"" + programSize + "\""; + a += " --noeditor --log temp.log \"" + postPath + "\" \"" + intermediatePath + "\" \"" + FileSystem.replaceExtension(outputPath, "xlsx") + "\""; + execute(getPostProcessorPath(), a, false, ""); + executeNoWait("excel", "\"" + FileSystem.replaceExtension(outputPath, "xlsx") + "\"", false, ""); +} +*/ diff --git a/Haas_Next_Generation/haas next generation.cps b/Haas_Next_Generation/haas next generation.cps new file mode 100644 index 0000000..8d85afc --- /dev/null +++ b/Haas_Next_Generation/haas next generation.cps @@ -0,0 +1,3545 @@ +/** + Copyright (C) 2012-2019 by Autodesk, Inc. + All rights reserved. + + HAAS post processor configuration. + + $Revision: 42601 95324a72443c22406fe893c91f874e4f0971d5c4 $ + $Date: 2019-12-04 06:35:42 $ + + FORKID {DBD402DA-DE90-4634-A6A3-0AE5CC97DEC7} +*/ + +description = "HAAS - Next Generation Control"; +vendor = "Haas Automation"; +vendorUrl = "https://www.haascnc.com"; +legal = "Copyright (C) 2012-2019 by Autodesk, Inc."; +certificationLevel = 2; +minimumRevision = 40783; + +longDescription = "Generic post for the HAAS Next Generation control. The post includes support for multi-axis indexing and simultaneous machining. The post utilizes the dynamic work offset feature so you can place your work piece as desired without having to repost your NC programs."; + +extension = "nc"; +programNameIsInteger = true; +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(355); +allowHelicalMoves = true; +allowedCircularPlanes = undefined; // allow any circular motion +allowSpiralMoves = true; +highFeedrate = (unit == IN) ? 650 : 5000; // up to 650 should be supported + +// user-defined properties +properties = { + writeMachine: false, // write machine + writeTools: true, // writes the tools + writeVersion: true, // include version info + preloadTool: true, // preloads next tool on tool change if any + chipTransport: false, // turn on chip transport at start of program + showSequenceNumbers: true, // show sequence numbers + sequenceNumberStart: 10, // first sequence number + sequenceNumberIncrement: 5, // increment for sequence numbers + sequenceNumberOnlyOnToolChange: false, // only output sequence numbers on tool change + optionalStop: true, // optional stop + separateWordsWithSpace: true, // specifies that the words should be separated with a white space + useRadius: false, // specifies that arcs should be output using the radius (R word) instead of the I, J, and K words. + useParametricFeed: false, // specifies that feed should be output using Q values + showNotes: true, // specifies that operation notes should be output + useG0: true, // allow G0 when moving along more than one axis + useG28: false, // specifies that G28 should be used instead of G53 + useSubroutines: false, // specifies that subroutines should be generated + useSubroutinePatterns: false, // generates subroutines for patterned operation + useSubroutineCycles: false, // generates subroutines for cycle operations on same holes + useG187: false, // use G187 to set smoothing on the machine + homePositionCenter: true, // moves the part in X in center of the door at end of program (ONLY WORKS IF THE TABLE IS MOVING) + optionallyCycleToolsAtStart: false, // cycle through each tool used at the beginning of the program when block delete is turned off - this allows the operator to easily measure all tools before they are used for the first run of the program + optionallyMeasureToolsAtStart: false, // measure each tool used at the beginning of the program when block delete is turned off - this allows the operator to easily measure all tools before they are used for the first run of the program + hasAAxis: "false", // configures A axis + hasBAxis: "false", // configures B axis + hasCAxis: "false", // configures C axis + toolBreakageTolerance: 0.1, // value for which tool break detection will raise an alarm + safeStartAllOperations: false, // write optional blocks at the beginning of all operations that include all commands to start program + fastToolChange: false, // skip spindle off, coolant off, and Z retract to make tool change quicker + useG95forTapping: false, // use IPR/MPR instead of IPM/MPM for tapping + safeRetractDistance: 0.0, // distance to add to retract distance when rewinding rotary axes + useDPMFeeds: false, // output DPM feeds instead of Inverse Time feeds + useTCPC: true, // enable/disable TCPC option + useDWO: true // Dynamic Work Offset (DWO), like CYCL 19 +}; + +propertyDefinitions = { + writeMachine: {title:"Write machine", description:"Output the machine settings in the header of the code.", group:0, type:"boolean"}, + writeTools: {title:"Write tool list", description:"Output a tool list in the header of the code.", group:0, type:"boolean"}, + writeVersion: {title:"Write version", description:"Write the version number in the header of the code.", group:0, type:"boolean"}, + preloadTool: {title:"Preload tool", description:"Preloads the next tool at a tool change (if any).", type:"boolean"}, + chipTransport: {title:"Use chip transport", description:"Enable to turn on chip transport at start of program.", type:"boolean"}, + showSequenceNumbers: {title:"Use sequence numbers", description:"Use sequence numbers for each block of outputted code.", group:1, type:"boolean"}, + sequenceNumberStart: {title:"Start sequence number", description:"The number at which to start the sequence numbers.", group:1, type:"integer"}, + sequenceNumberIncrement: {title:"Sequence number increment", description:"The amount by which the sequence number is incremented by in each block.", group:1, type:"integer"}, + sequenceNumberOnlyOnToolChange: {title:"Block number only on tool change", description:"Specifies that block numbers should only be output at tool changes.", type:"boolean"}, + optionalStop: {title:"Optional stop", description:"Specifies that optional stops M1 should be output at tool changes.", type:"boolean"}, + separateWordsWithSpace: {title:"Separate words with space", description:"Adds spaces between words if 'yes' is selected.", type:"boolean"}, + useRadius: {title:"Radius arcs", description:"If yes is selected, arcs are output using radius values rather than IJK.", type:"boolean"}, + useParametricFeed: {title:"Parametric feed", description:"Parametric feed values based on movement type are output.", type:"boolean"}, + showNotes: {title:"Show notes", description:"Enable to output notes for operations.", type:"boolean"}, + useG0: {title:"Use G0", description:"Specifies that G0s should be used for rapid moves when moving along a single axis.", type:"boolean"}, + useG28: {title:"Use G28 instead of G53", description:"Specifies that machine retracts should be done using G28 instead of G53.", type:"boolean"}, + useSubroutines: {title:"Use subroutines", description:"Enables output of subroutines for each operation.", type:"boolean"}, + useSubroutinePatterns: {title:"Subroutines for patterns", description:"Enable output of subroutines for patterns.", type:"boolean"}, + useSubroutineCycles: {title:"Subroutines for cycles", description:"Enable output of subroutines for cycles.", type:"boolean"}, + useG187: {title:"Use G187", description:"Specifies that smoothing using G187 should be used.", type:"boolean"}, + homePositionCenter: {title:"Home position center", description:"Enable to center the part along X at the end of program for easy access. Requires a CNC with a moving table.", type:"boolean"}, + optionallyCycleToolsAtStart: {title:"Optionally cycle tools at start", description:"Cycle through each tool used at the beginning of the program when block delete is turned off.", type:"boolean"}, + optionallyMeasureToolsAtStart: {title:"Optionally measure tools at start", description:"Measure each tool used at the beginning of the program when block delete is turned off.", type:"boolean"}, + hasAAxis: { + title: "Has A-axis rotary", + description: "Enable if the machine has an A-axis table/trunnion. Check the table direction on the machine and use the (Reversed) selection if the table is moving in the opposite direction.", + type: "enum", + values:[ + {title:"No", id:"false"}, + {title:"Yes", id:"true"}, + {title:"Reversed", id:"reversed"} + ] + }, + hasBAxis: { + title: "Has B-axis rotary", + description: "Enable if the machine has a B-axis table/trunnion. Check the table direction on the machine and use the (Reversed) selection if the table is moving in the opposite direction.", + type: "enum", + values:[ + {title:"No", id:"false"}, + {title:"Yes", id:"true"}, + {title:"Reversed", id:"reversed"} + ] + }, + hasCAxis: { + title: "Has C-axis rotary", + description: "Enable if the machine has a C-axis table. Specifies a trunnion setup if an A-axis or B-axis is defined. Check the table direction on the machine and use the (Reversed) selection if the table is moving in the opposite direction.", + type: "enum", + values:[ + {title:"No", id:"false"}, + {title:"Yes", id:"true"}, + {title:"Reversed", id:"reversed"} + ] + }, + toolBreakageTolerance: {title:"Tool breakage tolerance", description:"Specifies the tolerance for which tool break detection will raise an alarm.", type:"spatial"}, + safeStartAllOperations: {title:"Safe start all operations", description:"Write optional blocks at the beginning of all operations that include all commands to start program.", type:"boolean"}, + fastToolChange: {title:"Fast tool change", description:"Skip spindle off, coolant off, and Z retract to make tool change quicker.", type:"boolean"}, + useG95forTapping: {title:"Use G95 for tapping", description:"use IPR/MPR instead of IPM/MPM for tapping", type:"boolean"}, + safeRetractDistance: {title:"Safe retract distance", description:"Specifies the distance to add to retract distance when rewinding rotary axes.", type:"spatial"}, + useDPMFeeds: {title:"Rotary moves use IPM feeds", description:"Enable to output IPM feeds, disable for Inverse Time feeds with rotary axes moves.", type:"boolean"}, + useTCPC: {title:"Use TCPC programming", description:"The control supports Tool Center Point Control programming.", type:"boolean"}, + useDWO: {title:"Use DWO", description:"Specifies that the Dynamic Work Offset feature (G254/G255) should be used.", type:"boolean"} +}; + +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}, + {id: COOLANT_THROUGH_TOOL, on: 88, off: 89}, + {id: COOLANT_AIR, on: 83, off: 84}, + {id: COOLANT_AIR_THROUGH_TOOL, on: 73, off: 74}, + {id: COOLANT_SUCTION}, + {id: COOLANT_FLOOD_MIST}, + {id: COOLANT_FLOOD_THROUGH_TOOL, on: [88, 8], off: [89, 9]}, + {id: COOLANT_OFF, off: 9} +]; + +// old machines only support 4 digits +var oFormat = createFormat({width:5, zeropad:true, decimals:0}); +var nFormat = createFormat({decimals:0}); + +var gFormat = createFormat({prefix:"G", decimals:0}); +var mFormat = createFormat({prefix:"M", decimals:0}); +var hFormat = createFormat({prefix:"H", decimals:0}); +var dFormat = createFormat({prefix:"D", decimals:0}); +var probe154Format = createFormat({decimals:0, zeropad:true, width:2}); + +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-1000 +var milliFormat = createFormat({decimals:0}); // milliseconds // range 1-9999 +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 inverseTimeOutput = createVariable({prefix:"F", force:true}, feedFormat); +var pitchOutput = createVariable({prefix:"F", force:true}, pitchFormat); +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({force:true}, gFormat); // modal group 10 // G98-99 +var gRotationModal = createModal({}, gFormat); // modal group 16 // G68-G69 + +// fixed settings +var firstFeedParameter = 100; // the first variable to use with parametric feed +var forceResetWorkPlane = false; // enable to force reset of machine ABC on new orientation +var minimumCyclePoints = 5; // minimum number of points in cycle operation to consider for subprogram +var useDwoForPositioning = true; // specifies to use the DWO feature for XY positioning for multi-axis operations + +var WARNING_WORK_OFFSET = 0; + +var ANGLE_PROBE_NOT_SUPPORTED = 0; +var ANGLE_PROBE_USE_ROTATION = 1; +var ANGLE_PROBE_USE_CAXIS = 2; + +var SUB_UNKNOWN = 0; +var SUB_PATTERN = 1; +var SUB_CYCLE = 2; + +// collected state +var sequenceNumber; +var currentWorkOffset; +var optionalSection = false; +var forceSpindleSpeed = false; +var activeMovements; // do not use by default +var currentFeedId; +var maximumCircularRadiiDifference = toPreciseUnit(0.005, MM); +var maximumLineLength = 80; // the maximum number of charaters allowed in a line +var g68RotationMode = 0; +var angularProbingMode; +var subprograms = []; +var currentPattern = -1; +var firstPattern = false; +var currentSubprogram; +var lastSubprogram; +var initialSubprogramNumber = 90000; +var definedPatterns = new Array(); +var incrementalMode = false; +var saveShowSequenceNumbers; +var cycleSubprogramIsActive = false; +var patternIsActive = false; +var lastOperationComment = ""; +var incrementalSubprogram; +var retracted = false; // specifies that the tool has been retracted to the safe plane +var hasA = false; +var hasB = false; +var hasC = false; +var measureTool = false; +probeMultipleFeatures = true; + +// used to convert blocks to optional for safeStartAllOperations, might get used outside of onSection +var operationNeedsSafeStart = false; +var operationNeedsSafeToolCall = false; +var operationNeedsSafeWorkOffset = false; +var operationNeedsSafeWorkPlane = false; +var operationNeedsSafeCoolant = false; + +/** + Writes the specified block. +*/ +var skipBlock = false; +function writeBlock() { + var text = formatWords(arguments); + if (!text) { + return; + } + var maximumSequenceNumber = (properties.useSubroutines || properties.useSubroutinePatterns || + properties.useSubroutineCycles) ? initialSubprogramNumber : 99999; + if (properties.showSequenceNumbers) { + if (sequenceNumber >= maximumSequenceNumber) { + sequenceNumber = properties.sequenceNumberStart; + } + if (optionalSection || skipBlock) { + if (text) { + writeWords("/", "N" + sequenceNumber, text); + } + } else { + writeWords2("N" + sequenceNumber, arguments); + } + sequenceNumber += properties.sequenceNumberIncrement; + } else { + if (optionalSection || skipBlock) { + writeWords2("/", arguments); + } else { + writeWords(arguments); + } + } + skipBlock = false; +} + +/** + Writes the specified block - used for tool changes only. +*/ +function writeToolBlock() { + var show = properties.showSequenceNumbers; + properties.showSequenceNumbers = show || properties.sequenceNumberOnlyOnToolChange; + writeBlock(arguments); + properties.showSequenceNumbers = show; +} + +/** + Writes the specified optional block. +*/ +function writeOptionalBlock() { + skipBlock = true; + writeBlock(arguments); +} + +function formatComment(text) { + return "(" + String(text).replace(/[()]/g, "") + ")"; +} + +/** + Output a comment. +*/ +function writeComment(text) { + writeln(formatComment(text.substr(0, maximumLineLength - 2))); +} + +/** + Returns the matching HAAS tool type for the tool. +*/ +function getHaasToolType(toolType) { + switch (toolType) { + case TOOL_DRILL: + case TOOL_REAMER: + return 1; // drill + case TOOL_TAP_RIGHT_HAND: + case TOOL_TAP_LEFT_HAND: + return 2; // tap + case TOOL_MILLING_FACE: + case TOOL_MILLING_SLOT: + case TOOL_BORING_BAR: + return 3; // shell mill + case TOOL_MILLING_END_FLAT: + case TOOL_MILLING_END_BULLNOSE: + case TOOL_MILLING_TAPERED: + case TOOL_MILLING_DOVETAIL: + return 4; // end mill + case TOOL_DRILL_SPOT: + case TOOL_MILLING_CHAMFER: + case TOOL_DRILL_CENTER: + case TOOL_COUNTER_SINK: + case TOOL_COUNTER_BORE: + case TOOL_MILLING_THREAD: + case TOOL_MILLING_FORM: + return 5; // center drill + case TOOL_MILLING_END_BALL: + case TOOL_MILLING_LOLLIPOP: + return 6; // ball nose + case TOOL_PROBE: + return 7; // probe + default: + error(localize("Invalid HAAS tool type.")); + return -1; + } +} + +function getHaasProbingType(toolType, use9023) { + switch (getHaasToolType(toolType)) { + case 3: + return (use9023 ? 23 : 1); // rotate + case 4: + return (use9023 ? 13 : 3); // rotate length and dia + case 1: + case 2: + case 5: + case 6: + case 7: + return (use9023 ? 12 : 2); // non rotate + case 0: + default: + error(localize("Invalid HAAS tool type.")); + return -1; + } +} + +function writeToolCycleBlock(tool) { + writeOptionalBlock("T" + toolFormat.format(tool.number), mFormat.format(6)); // get tool + writeOptionalBlock(mFormat.format(0)); // wait for operator +} + +function writeToolMeasureBlock(tool) { + var writeFunction = measureTool ? writeBlock : writeOptionalBlock; + var comment = measureTool ? formatComment("MEASURE TOOL") : ""; + if (true) { // use Macro P9023 to measure tools + var probingType = getHaasProbingType(tool.type, true); + writeFunction( + gFormat.format(65), + "P9023", + "A" + probingType + ".", + "T" + toolFormat.format(tool.number), + conditional((probingType != 12), "H" + xyzFormat.format(tool.bodyLength + tool.holderLength)), + conditional((probingType != 12), "D" + xyzFormat.format(tool.diameter)), + comment + ); + } else { // use Macro P9995 to measure tools + writeFunction("T" + toolFormat.format(tool.number), mFormat.format(6)); // get tool + writeFunction( + gFormat.format(65), + "P9995", + "A0.", + "B" + getHaasToolType(tool.type) + ".", + "C" + getHaasProbingType(tool.type, false) + ".", + "T" + toolFormat.format(tool.number), + "E" + xyzFormat.format(tool.bodyLength + tool.holderLength), + "D" + xyzFormat.format(tool.diameter), + "K" + xyzFormat.format(0.1), + "I0.", + comment + ); // probe tool + } + measureTool = false; +} + +function onOpen() { + if (properties.useDPMFeeds) { + gFeedModeModal.format(94); + } + if (properties.useRadius) { + maximumCircularSweep = toRad(90); // avoid potential center calculation errors for CNC + } + if (properties.sequenceNumberOnlyOnToolChange) { + properties.showSequenceNumbers = false; + } + if (!properties.useDWO) { + useDwoForPositioning = false; + } + + gRotationModal.format(69); // Default to G69 Rotation Off + + hasA = properties.hasAAxis != "false"; + hasB = properties.hasBAxis != "false"; + hasC = properties.hasCAxis != "false"; + + if (hasA && hasB && hasC) { + error(localize("Only two rotary axes can be active at the same time.")); + return; + } + + if (true) { // configure machine + var aAxis; + var bAxis; + var cAxis; + + if (hasA) { // A Axis - For horizontal machines and trunnions + var dir = properties.hasAAxis == "reversed" ? -1 : 1; + if (hasC || hasB) { + var aMin = (dir == 1) ? -120 - 0.0001 : -30 - 0.0001; + var aMax = (dir == 1) ? 30 + 0.0001 : 120 + 0.0001; + aAxis = createAxis({coordinate:0, table:true, axis:[dir, 0, 0], range:[aMin, aMax], preference:dir}); + } else { + aAxis = createAxis({coordinate:0, table:true, axis:[dir, 0, 0], cyclic:true}); + } + } + + if (hasB) { // B Axis - For horizontal machines and trunnions + var dir = properties.hasBAxis == "reversed" ? -1 : 1; + if (hasC) { + var bMin = (dir == 1) ? -120 - 0.0001 : -30 - 0.0001; + var bMax = (dir == 1) ? 30 + 0.0001 : 120 + 0.0001; + bAxis = createAxis({coordinate:1, table:true, axis:[0, dir, 0], range:[bMin, bMax], preference:-dir}); + } else if (hasA) { + bAxis = createAxis({coordinate:1, table:true, axis:[0, 0, dir], cyclic:true}); + } else { + bAxis = createAxis({coordinate:1, table:true, axis:[0, dir, 0], cyclic:true}); + } + } + + if (hasC) { // C Axis - For trunnions only + var dir = properties.hasCAxis == "reversed" ? -1 : 1; + cAxis = createAxis({coordinate:2, table:true, axis:[0, 0, dir], cyclic:true}); + } + + if (hasA && hasC) { // AC trunnion + machineConfiguration = new MachineConfiguration(aAxis, cAxis); + } else if (hasB && hasC) { // BC trunnion + machineConfiguration = new MachineConfiguration(bAxis, cAxis); + } else if (hasA && hasB) { // AB trunnion + machineConfiguration = new MachineConfiguration(aAxis, bAxis); + } else if (hasA) { // A rotary + machineConfiguration = new MachineConfiguration(aAxis); + } else if (hasB) { // B rotary - horizontal machine only + machineConfiguration = new MachineConfiguration(bAxis); + } + + if (hasA || hasB || hasC) { + setMachineConfiguration(machineConfiguration); + optimizeMachineAngles2(properties.useTCPC ? 0 : 1); // map tip mode + } + } + + if (!machineConfiguration.isMachineCoordinate(0)) { + aOutput.disable(); + } + if (!machineConfiguration.isMachineCoordinate(1)) { + bOutput.disable(); + } + if (!machineConfiguration.isMachineCoordinate(2)) { + cOutput.disable(); + } + + if (highFeedrate <= 0) { + error(localize("You must set 'highFeedrate' because axes are not synchronized for rapid traversal.")); + return; + } + + if (!properties.separateWordsWithSpace) { + setWordSeparator(""); + } + + sequenceNumber = properties.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 <= 99999))) { + error(localize("Program number is out of range.")); + return; + } + writeln( + "O" + oFormat.format(programId) + + conditional(programComment, " " + formatComment(programComment.substr(0, maximumLineLength - 2 - ("O" + oFormat.format(programId)).length - 1))) + ); + lastSubprogram = (initialSubprogramNumber - 1); + } else { + error(localize("Program name has not been specified.")); + return; + } + + if (properties.useG0) { + writeComment(localize("Using G0 which travels along dogleg path.")); + } else { + writeComment(subst(localize("Using high feed G1 F%1 instead of G0."), feedFormat.format(highFeedrate))); + } + + if (properties.writeVersion) { + if ((typeof getHeaderVersion == "function") && getHeaderVersion()) { + writeComment(localize("post version") + ": " + getHeaderVersion()); + } + if ((typeof getHeaderDate == "function") && getHeaderDate()) { + writeComment(localize("post modified") + ": " + getHeaderDate()); + } + } + + // dump machine configuration + var vendor = machineConfiguration.getVendor(); + var model = machineConfiguration.getModel(); + var description = machineConfiguration.getDescription(); + + if (properties.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); + } + } + + // dump tool information + if (properties.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); + } + } + } + + // optionally cycle through all tools + if (properties.optionallyCycleToolsAtStart || properties.optionallyMeasureToolsAtStart) { + var tools = getToolTable(); + if (tools.getNumberOfTools() > 0) { + writeln(""); + + writeOptionalBlock(mFormat.format(0), formatComment(localize("Read note"))); // wait for operator + writeComment(localize("With BLOCK DELETE turned off each tool will cycle through")); + writeComment(localize("the spindle to verify that the correct tool is in the tool magazine")); + if (properties.optionallyMeasureToolsAtStart) { + writeComment(localize("and to automatically measure it")); + } + writeComment(localize("Once the tools are verified turn BLOCK DELETE on to skip verification")); + + for (var i = 0; i < tools.getNumberOfTools(); ++i) { + var tool = tools.getTool(i); + if (properties.optionallyMeasureToolsAtStart && (tool.type == TOOL_PROBE)) { + continue; + } + 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"); + } + comment += " - " + getToolTypeName(tool.type); + writeComment(comment); + if (properties.optionallyMeasureToolsAtStart) { + writeOptionalBlock("T" + toolFormat.format(tool.number), mFormat.format(6)); //Changes Tool + writeOptionalBlock(mFormat.format(0), formatComment(localize("Load Tool"))); //Pause until operator loads tool + writeToolMeasureBlock(tool); + } else { + writeToolCycleBlock(tool); + } + } + writeOptionalBlock(mFormat.format(0), formatComment(localize("Operator Start Program"))); // wait for operator + } + writeln(""); + } + + if (false /*properties.useDWO*/) { + var failed = false; + var dynamicWCSs = {}; + for (var i = 0; i < getNumberOfSections(); ++i) { + var section = getSection(i); + var description = section.hasParameter("operation-comment") ? section.getParameter("operation-comment") : ("#" + (i + 1)); + if (!section.hasDynamicWorkOffset()) { + error(subst(localize("Dynamic work offset has not been set for operation '%1'."), description)); + failed = true; + } + + var o = section.getDynamicWCSOrigin(); + var p = section.getDynamicWCSPlane(); + if (dynamicWCSs[section.getDynamicWorkOffset()]) { + if ((Vector.diff(o, dynamicWCSs[section.getDynamicWorkOffset()].origin).length > 1e-9) || + (Matrix.diff(p, dynamicWCSs[section.getDynamicWorkOffset()].plane).n1 > 1e-9)) { + error(subst(localize("Dynamic WCS mismatch for operation '%1'."), description)); + failed = true; + } + } else { + dynamicWCSs[section.getDynamicWorkOffset()] = {origin:o, plane:p}; + } + } + if (failed) { + return; + } + } + + 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 and feed per min + writeBlock(gAbsIncModal.format(90), gFeedModeModal.format(94), gPlaneModal.format(17)); + + switch (unit) { + case IN: + writeBlock(gUnitModal.format(20)); + break; + case MM: + writeBlock(gUnitModal.format(21)); + break; + } + + if (properties.chipTransport) { + onCommand(COMMAND_START_CHIP_TRANSPORT); + } + // Probing Surface Inspection + if (typeof inspectionWriteVariables == "function") { + inspectionWriteVariables(); + } +} + +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; + feedOutput.reset(); +} + +/** Force output of X, Y, Z, A, B, C, and F on next output. */ +function forceAny() { + forceXYZ(); + forceABC(); + forceFeed(); +} + +function writeG187() { + if (hasParameter("operation-strategy") && (getParameter("operation-strategy") == "drill")) { + writeBlock(gFormat.format(187)); // reset G187 setting to machine default + } else if (hasParameter("operation:tolerance")) { + var tolerance = Math.max(getParameter("operation:tolerance"), 0); + if (tolerance > 0) { + var stockToLeaveThreshold = toUnit(0.1, MM); + var stockToLeave = 0; + var verticalStockToLeave = 0; + if (hasParameter("operation:stockToLeave")) { + stockToLeave = xyzFormat.getResultingValue(getParameter("operation:stockToLeave")); + } + if (hasParameter("operation:verticalStockToLeave")) { + verticalStockToLeave = xyzFormat.getResultingValue(getParameter("operation:verticalStockToLeave")); + } + + var workMode; + if (((stockToLeave > stockToLeaveThreshold) && (verticalStockToLeave > stockToLeaveThreshold)) || + (hasParameter("operation:strategy") && getParameter("operation:strategy") == "face")) { + workMode = 1; // roughing + } else { + if ((stockToLeave > 0) || (verticalStockToLeave > 0)) { + workMode = 2; // default + } else { + workMode = 3; // fine + } + } + writeBlock(gFormat.format(187), "P" + workMode); // set tolerance mode + // writeBlock(gFormat.format(187), "P" + workMode, "E" + xyzFormat.format(tolerance)); // set tolerance mode + } else { + writeBlock(gFormat.format(187)); // reset G187 setting to machine default + } + } else { + writeBlock(gFormat.format(187)); // reset G187 setting to machine default + } +} + +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)) { + var feedContext = new FeedContext(id, localize("High Feed"), this.highFeedrate); + activeFeeds.push(feedContext); + activeMovements[MOVEMENT_HIGH_FEED] = 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 activeG254 = false; + +function forceWorkPlane() { + currentWorkPlaneABC = undefined; +} + +function defineWorkPlane(_section, _setWorkPlane) { + var abc = new Vector(0, 0, 0); + if (machineConfiguration.isMultiAxisConfiguration()) { // use 5-axis indexing for multi-axis mode + // set working plane after datum shift + + if (_section.isMultiAxis()) { + cancelTransformation(); + abc = _section.getInitialToolAxisABC(); + if (_setWorkPlane) { + if (activeG254) { + writeBlock(gFormat.format(255)); // cancel DWO + activeG254 = false; + } + if (!retracted) { + writeRetract(Z); + } + forceWorkPlane(); + onCommand(COMMAND_UNLOCK_MULTI_AXIS); + 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 { + abc = getWorkPlaneMachineABC(_section.workPlane, _setWorkPlane); + if (_setWorkPlane) { + setWorkPlane(abc); + } + } + } else { // pure 3D + var remaining = _section.workPlane; + if (!isSameDirection(remaining.forward, new Vector(0, 0, 1))) { + error(localize("Tool orientation is not supported.")); + return abc; + } + setRotation(remaining); + } + return abc; +} + +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))) { + return; // no change + } + + onCommand(COMMAND_UNLOCK_MULTI_AXIS); + + if (activeG254) { + writeBlock(operationNeedsSafeWorkPlane ? "/" : "", gFormat.format(255)); // cancel DWO + activeG254 = false; + } + + gMotionModal.reset(); + writeBlock( + operationNeedsSafeWorkPlane ? "/" : "", + 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)) + ); + + onCommand(COMMAND_LOCK_MULTI_AXIS); + + if (properties.useDWO && + (abcFormat.isSignificant(abc.x) || abcFormat.isSignificant(abc.y) || abcFormat.isSignificant(abc.z))) { + writeBlock(operationNeedsSafeWorkPlane ? "/" : "", gFormat.format(254)); // enable DWO + activeG254 = true; + } + + currentWorkPlaneABC = abc; +} + +var closestABC = false; // choose closest machine angles +var currentMachineABC; + +function getWorkPlaneMachineABC(workPlane, _setWorkPlane) { + var W = workPlane; // map to global frame + + var abc = machineConfiguration.getABC(W); + if (closestABC) { + if (currentMachineABC) { + abc = machineConfiguration.remapToABC(abc, currentMachineABC); + } else { + abc = machineConfiguration.getPreferredABC(abc); + } + } else { + abc = machineConfiguration.getPreferredABC(abc); + } + + try { + abc = machineConfiguration.remapABC(abc); + if (_setWorkPlane) { + 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; + if (tcp) { + setRotation(W); // TCP mode + } else { + var O = machineConfiguration.getOrientation(abc); + var R = machineConfiguration.getRemainingOrientation(abc, W); + setRotation(R); + } + + return abc; +} + +function isProbeOperation() { + return hasParameter("operation-strategy") && ((getParameter("operation-strategy") == "probe" || getParameter("operation-strategy") == "probe_geometry")); +} + +function isInspectionOperation(section) { + return section.hasParameter("operation-strategy") && (section.getParameter("operation-strategy") == "inspectSurface"); +} + +var probeOutputWorkOffset = 1; + +function onPassThrough(text) { + var commands = String(text).split(","); + for (text in commands) { + writeBlock(commands[text]); + } +} + +function onParameter(name, value) { + if (name == "probe-output-work-offset") { + probeOutputWorkOffset = (value > 0) ? value : 1; + } +} + +/** Returns true if the spatial vectors are significantly different. */ +function areSpatialVectorsDifferent(_vector1, _vector2) { + return (xyzFormat.getResultingValue(_vector1.x) != xyzFormat.getResultingValue(_vector2.x)) || + (xyzFormat.getResultingValue(_vector1.y) != xyzFormat.getResultingValue(_vector2.y)) || + (xyzFormat.getResultingValue(_vector1.z) != xyzFormat.getResultingValue(_vector2.z)); +} + +/** Returns true if the spatial boxes are a pure translation. */ +function areSpatialBoxesTranslated(_box1, _box2) { + return !areSpatialVectorsDifferent(Vector.diff(_box1[1], _box1[0]), Vector.diff(_box2[1], _box2[0])) && + !areSpatialVectorsDifferent(Vector.diff(_box2[0], _box1[0]), Vector.diff(_box2[1], _box1[1])); +} + +/** Returns true if the spatial boxes are same. */ +function areSpatialBoxesSame(_box1, _box2) { + return !areSpatialVectorsDifferent(_box1[0], _box2[0]) && !areSpatialVectorsDifferent(_box1[1], _box2[1]); +} + +function subprogramDefine(_initialPosition, _abc, _retracted, _zIsOutput) { + // convert patterns into subprograms + var usePattern = false; + patternIsActive = false; + if (currentSection.isPatterned && currentSection.isPatterned() && properties.useSubroutinePatterns) { + currentPattern = currentSection.getPatternId(); + firstPattern = true; + for (var i = 0; i < definedPatterns.length; ++i) { + if ((definedPatterns[i].patternType == SUB_PATTERN) && (currentPattern == definedPatterns[i].patternId)) { + currentSubprogram = definedPatterns[i].subProgram; + usePattern = definedPatterns[i].validPattern; + firstPattern = false; + break; + } + } + + if (firstPattern) { + // determine if this is a valid pattern for creating a subprogram + usePattern = subprogramIsValid(currentSection, currentPattern, SUB_PATTERN); + if (usePattern) { + currentSubprogram = ++lastSubprogram; + } + definedPatterns.push({ + patternType: SUB_PATTERN, + patternId: currentPattern, + subProgram: currentSubprogram, + validPattern: usePattern, + initialPosition: _initialPosition, + finalPosition: _initialPosition + }); + } + + if (usePattern) { + // make sure Z-position is output prior to subprogram call + if (!_retracted && !_zIsOutput) { + writeBlock(gMotionModal.format(0), zOutput.format(_initialPosition.z)); + } + + // call subprogram + writeBlock(mFormat.format(97), "P" + nFormat.format(currentSubprogram)); + patternIsActive = true; + + if (firstPattern) { + subprogramStart(_initialPosition, _abc, incrementalSubprogram); + } else { + skipRemainingSection(); + setCurrentPosition(getFramePosition(currentSection.getFinalPosition())); + } + } + } + + // Output cycle operation as subprogram + if (!usePattern && properties.useSubroutineCycles && currentSection.doesStrictCycle && + (currentSection.getNumberOfCycles() == 1) && currentSection.getNumberOfCyclePoints() >= minimumCyclePoints) { + var finalPosition = getFramePosition(currentSection.getFinalPosition()); + currentPattern = currentSection.getNumberOfCyclePoints(); + firstPattern = true; + for (var i = 0; i < definedPatterns.length; ++i) { + if ((definedPatterns[i].patternType == SUB_CYCLE) && (currentPattern == definedPatterns[i].patternId) && + !areSpatialVectorsDifferent(_initialPosition, definedPatterns[i].initialPosition) && + !areSpatialVectorsDifferent(finalPosition, definedPatterns[i].finalPosition)) { + currentSubprogram = definedPatterns[i].subProgram; + usePattern = definedPatterns[i].validPattern; + firstPattern = false; + break; + } + } + + if (firstPattern) { + // determine if this is a valid pattern for creating a subprogram + usePattern = subprogramIsValid(currentSection, currentPattern, SUB_CYCLE); + if (usePattern) { + currentSubprogram = ++lastSubprogram; + } + definedPatterns.push({ + patternType: SUB_CYCLE, + patternId: currentPattern, + subProgram: currentSubprogram, + validPattern: usePattern, + initialPosition: _initialPosition, + finalPosition: finalPosition + }); + } + cycleSubprogramIsActive = usePattern; + } + + // Output each operation as a subprogram + if (!usePattern && properties.useSubroutines) { + currentSubprogram = ++lastSubprogram; + writeBlock(mFormat.format(97), "P" + nFormat.format(currentSubprogram)); + firstPattern = true; + subprogramStart(_initialPosition, _abc, false); + } +} + +function subprogramStart(_initialPosition, _abc, _incremental) { + redirectToBuffer(); + var comment = ""; + if (hasParameter("operation-comment")) { + comment = getParameter("operation-comment"); + } + writeln( + "N" + nFormat.format(currentSubprogram) + + conditional(comment, formatComment(comment.substr(0, maximumLineLength - 2 - 6 - 1))) + ); + saveShowSequenceNumbers = properties.showSequenceNumbers; + properties.showSequenceNumbers = false; + if (_incremental) { + setIncrementalMode(_initialPosition, _abc); + } + gPlaneModal.reset(); + gMotionModal.reset(); +} + +function subprogramEnd() { + if (firstPattern) { + writeBlock(mFormat.format(99)); + writeln(""); + subprograms += getRedirectionBuffer(); + } + forceAny(); + firstPattern = false; + properties.showSequenceNumbers = saveShowSequenceNumbers; + closeRedirection(); +} + +function subprogramIsValid(_section, _patternId, _patternType) { + var sectionId = _section.getId(); + var numberOfSections = getNumberOfSections(); + var validSubprogram = _patternType != SUB_CYCLE; + + var masterPosition = new Array(); + masterPosition[0] = getFramePosition(_section.getInitialPosition()); + masterPosition[1] = getFramePosition(_section.getFinalPosition()); + var tempBox = _section.getBoundingBox(); + var masterBox = new Array(); + masterBox[0] = getFramePosition(tempBox[0]); + masterBox[1] = getFramePosition(tempBox[1]); + + var rotation = getRotation(); + var translation = getTranslation(); + incrementalSubprogram = undefined; + + for (var i = 0; i < numberOfSections; ++i) { + var section = getSection(i); + if (section.getId() != sectionId) { + defineWorkPlane(section, false); + // check for valid pattern + if (_patternType == SUB_PATTERN) { + if (section.getPatternId() == _patternId) { + var patternPosition = new Array(); + patternPosition[0] = getFramePosition(section.getInitialPosition()); + patternPosition[1] = getFramePosition(section.getFinalPosition()); + tempBox = section.getBoundingBox(); + var patternBox = new Array(); + patternBox[0] = getFramePosition(tempBox[0]); + patternBox[1] = getFramePosition(tempBox[1]); + + if (areSpatialBoxesSame(masterPosition, patternPosition) && areSpatialBoxesSame(masterBox, patternBox)) { + incrementalSubprogram = incrementalSubprogram ? incrementalSubprogram : false; + } else if (!areSpatialBoxesTranslated(masterPosition, patternPosition) || !areSpatialBoxesTranslated(masterBox, patternBox)) { + validSubprogram = false; + break; + } else { + incrementalSubprogram = true; + } + } + + // check for valid cycle operation + } else if (_patternType == SUB_CYCLE) { + if ((section.getNumberOfCyclePoints() == _patternId) && (section.getNumberOfCycles() == 1)) { + var patternInitial = getFramePosition(section.getInitialPosition()); + var patternFinal = getFramePosition(section.getFinalPosition()); + if (!areSpatialVectorsDifferent(patternInitial, masterPosition[0]) && !areSpatialVectorsDifferent(patternFinal, masterPosition[1])) { + validSubprogram = true; + break; + } + } + } + } + } + setRotation(rotation); + setTranslation(translation); + return (validSubprogram); +} + +function setAxisMode(_format, _output, _prefix, _value, _incr) { + var i = _output.isEnabled(); + _output = _incr ? createIncrementalVariable({prefix: _prefix}, _format) : createVariable({prefix: _prefix}, _format); + _output.format(_value); + _output.format(_value); + i = i ? _output.enable() : _output.disable(); + return _output; +} + +function setIncrementalMode(xyz, abc) { + xOutput = setAxisMode(xyzFormat, xOutput, "X", xyz.x, true); + yOutput = setAxisMode(xyzFormat, yOutput, "Y", xyz.y, true); + zOutput = setAxisMode(xyzFormat, zOutput, "Z", xyz.z, true); + aOutput = setAxisMode(abcFormat, aOutput, "A", abc.x, true); + bOutput = setAxisMode(abcFormat, bOutput, "B", abc.y, true); + cOutput = setAxisMode(abcFormat, cOutput, "C", abc.z, true); + gAbsIncModal.reset(); + writeBlock(gAbsIncModal.format(91)); + incrementalMode = true; +} + +function setAbsoluteMode(xyz, abc) { + if (incrementalMode) { + xOutput = setAxisMode(xyzFormat, xOutput, "X", xyz.x, false); + yOutput = setAxisMode(xyzFormat, yOutput, "Y", xyz.y, false); + zOutput = setAxisMode(xyzFormat, zOutput, "Z", xyz.z, false); + aOutput = setAxisMode(abcFormat, aOutput, "A", abc.x, false); + bOutput = setAxisMode(abcFormat, bOutput, "B", abc.y, false); + cOutput = setAxisMode(abcFormat, cOutput, "C", abc.z, false); + gAbsIncModal.reset(); + writeBlock(gAbsIncModal.format(90)); + incrementalMode = false; + } +} + +function onSection() { + var forceToolAndRetract = optionalSection && !currentSection.isOptional(); + optionalSection = currentSection.isOptional(); + + var insertToolCall = isFirstSection() || + currentSection.getForceToolChange && currentSection.getForceToolChange() || + (tool.number != getPreviousSection().getTool().number); + + retracted = false; + + var zIsOutput = false; // true if the Z-position has been output, used for patterns + 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 (properties.safeStartAllOperations && !isFirstSection()) { // determine what needs to be included in safe start + if (!insertToolCall) { // tool call required + operationNeedsSafeToolCall = true; + forceSpindleSpeed = true; + } + if (!newWorkOffset) { // offset required + operationNeedsSafeWorkOffset = true; + } + if (!newWorkPlane) { // workplane required + operationNeedsSafeWorkPlane = true; + } + operationNeedsSafeStart = true; // operation will contain safe start components + } + + if ((insertToolCall && !properties.fastToolChange) || newWorkOffset || newWorkPlane || toolChecked) { + + // stop spindle before retract during tool change + if (insertToolCall && !isFirstSection() && !toolChecked && !properties.fastToolChange) { + onCommand(COMMAND_STOP_SPINDLE); + } + + // retract to safe plane + writeRetract(Z); + + if (forceResetWorkPlane && newWorkPlane) { + forceWorkPlane(); + setWorkPlane(new Vector(0, 0, 0)); // reset working plane + } + } + + if (hasParameter("operation-comment")) { + var comment = getParameter("operation-comment"); + if (comment && ((comment !== lastOperationComment) || !patternIsActive || insertToolCall)) { + writeln(""); + writeComment(comment); + lastOperationComment = comment; + } else if (!patternIsActive || insertToolCall) { + writeln(""); + } + } else { + writeln(""); + } + + if (operationNeedsSafeStart && !retracted) { // retract before safestart operations + skipBlock = true; + writeRetract(Z); + } + + if (properties.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 || operationNeedsSafeStart) { + forceWorkPlane(); + + if (properties.fastToolChange && !isProbeOperation()) { + currentCoolantMode = COOLANT_OFF; + } else if (!operationNeedsSafeToolCall) { // no coolant off command if safe start operation + onCommand(COMMAND_COOLANT_OFF); + } + + if (!isFirstSection() && properties.optionalStop) { + onCommand(COMMAND_OPTIONAL_STOP); + } + + if ((tool.number > 200 && tool.number < 1000) || tool.number > 9999) { + warning(localize("Tool number out of range.")); + } + + skipBlock = operationNeedsSafeToolCall; + writeToolBlock( + "T" + toolFormat.format(tool.number), + mFormat.format(6) + ); + if (tool.comment) { + writeComment(tool.comment); + } + if (measureTool) { + writeToolMeasureBlock(tool); + } + 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") + "=" + xyzFormat.format(zRange.getMinimum())); + } + } + } + + // activate those two coolant modes before the spindle is turned on + if ((tool.coolant == COOLANT_THROUGH_TOOL) || (tool.coolant == COOLANT_AIR_THROUGH_TOOL) || (tool.coolant == COOLANT_FLOOD_THROUGH_TOOL)) { + if (!isFirstSection() && !insertToolCall && (currentCoolantMode != tool.coolant)) { + onCommand(COMMAND_STOP_SPINDLE); + forceSpindleSpeed = true; + } + setCoolant(tool.coolant); + } else if ((currentCoolantMode == COOLANT_THROUGH_TOOL) || (currentCoolantMode == COOLANT_AIR_THROUGH_TOOL) || (currentCoolantMode == COOLANT_FLOOD_THROUGH_TOOL)) { + onCommand(COMMAND_STOP_SPINDLE); + setCoolant(COOLANT_OFF); + forceSpindleSpeed = true; + } + + if (toolChecked) { + forceSpindleSpeed = true; // spindle must be restarted if tool is checked without a tool change + toolChecked = false; // state of tool is not known at the beginning of a section since it could be broken for the previous section + } + + if (!isInspectionOperation(currentSection) && !isProbeOperation() && + (insertToolCall || + forceSpindleSpeed || + isFirstSection() || + (rpmFormat.areDifferent(spindleSpeed, sOutput.getCurrent())) || + (tool.clockwise != getPreviousSection().getTool().clockwise))) { + forceSpindleSpeed = false; + + if (spindleSpeed < 1) { + error(localize("Spindle speed out of range.")); + return; + } + if (spindleSpeed > 15000) { + warning(localize("Spindle speed exceeds maximum value.")); + } + writeBlock( + sOutput.format(spindleSpeed), mFormat.format(tool.clockwise ? 3 : 4) + ); + } + + if (properties.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; + } + + // wcs + if (insertToolCall || operationNeedsSafeWorkOffset) { // 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 (workOffset > 6) { + var code = workOffset - 6; + if (code > 99) { + error(localize("Work offset out of range.")); + return; + } + if (workOffset != currentWorkOffset) { + forceWorkPlane(); + writeBlock(gFormat.format(154), "P" + code); + currentWorkOffset = workOffset; + } + } else { + if (workOffset != currentWorkOffset) { + forceWorkPlane(); + writeBlock(gFormat.format(53 + workOffset)); // G54->G59 + currentWorkOffset = workOffset; + } + } + } + + if (newWorkPlane || (insertToolCall && !retracted)) { // go to home position for safety + if (!retracted) { + writeRetract(Z); + } + } + + forceXYZ(); + + // Unwind axis if previous section was Multi-Axis + if (!isFirstSection() && getPreviousSection().isMultiAxis() && hasC) { + writeBlock(gFormat.format(28), gAbsIncModal.format(91), "C" + abcFormat.format(0)); + writeBlock(gAbsIncModal.format(90)); + } + + if (newWorkOffset) { + forceWorkPlane(); + } + + var abc = defineWorkPlane(currentSection, true); + + // set coolant after we have positioned at Z + setCoolant(tool.coolant); + + forceAny(); + gMotionModal.reset(); + + if (properties.useG187) { + writeG187(); + } + + var initialPosition = getFramePosition(currentSection.getInitialPosition()); + if (!retracted && !insertToolCall) { + if (getCurrentPosition().z < initialPosition.z) { + writeBlock(gMotionModal.format(0), zOutput.format(initialPosition.z)); + zIsOutput = true; + } + } + + if (insertToolCall || retracted || operationNeedsSafeStart || + (!isFirstSection() && (currentSection.isMultiAxis() != getPreviousSection().isMultiAxis()))) { + var lengthOffset = tool.lengthOffset; + if ((lengthOffset > 200 && lengthOffset < 1000) || lengthOffset > 9999) { + error(localize("Length offset out of range.")); + return; + } + + gMotionModal.reset(); + writeBlock(gPlaneModal.format(17)); + + if (!machineConfiguration.isHeadConfiguration()) { + if ((currentSection.getOptimizedTCPMode() == 0) && useDwoForPositioning && currentSection.isMultiAxis()) { + var O = machineConfiguration.getOrientation(abc); + var initialPositionDWO = O.getTransposed().multiply(getGlobalPosition(currentSection.getInitialPosition())); + // writeComment("PREPOSITIONING START"); + writeBlock(gFormat.format(254)); + writeBlock(gAbsIncModal.format(90), gMotionModal.format(0), xOutput.format(initialPositionDWO.x), yOutput.format(initialPositionDWO.y)); + writeBlock(gFormat.format(255)); + // writeComment("PREPOSITIONING END"); + writeBlock( + gMotionModal.format(0), + conditional(!currentSection.isMultiAxis() || (currentSection.getOptimizedTCPMode() == 1), gFormat.format(43)), + conditional(currentSection.isMultiAxis() && (currentSection.getOptimizedTCPMode() == 0), gFormat.format(234)), + xOutput.format(initialPosition.x), yOutput.format(initialPosition.y), zOutput.format(initialPosition.z), + hFormat.format(lengthOffset) + ); + } else { + writeBlock(gAbsIncModal.format(90), gMotionModal.format(0), xOutput.format(initialPosition.x), yOutput.format(initialPosition.y)); + writeBlock( + gMotionModal.format(0), + conditional(!currentSection.isMultiAxis() || (currentSection.getOptimizedTCPMode() == 1), gFormat.format(43)), + conditional(currentSection.isMultiAxis() && (currentSection.getOptimizedTCPMode() == 0), gFormat.format(234)), + zOutput.format(initialPosition.z), + hFormat.format(lengthOffset) + ); + } + } else { + writeBlock( + gAbsIncModal.format(90), + gMotionModal.format(0), + conditional(!currentSection.isMultiAxis() || (currentSection.getOptimizedTCPMode() == 1), gFormat.format(43)), + conditional(currentSection.isMultiAxis() && (currentSection.getOptimizedTCPMode() == 0), gFormat.format(234)), + xOutput.format(initialPosition.x), + yOutput.format(initialPosition.y), + zOutput.format(initialPosition.z), + hFormat.format(lengthOffset) + ); + } + zIsOutput = true; + + gMotionModal.reset(); + } else { + var x = xOutput.format(initialPosition.x); + var y = yOutput.format(initialPosition.y); + if (!properties.useG0 && x && y) { + // axes are not synchronized + writeBlock(gAbsIncModal.format(90), gMotionModal.format(1), x, y, getFeed(highFeedrate)); + } else { + writeBlock(gAbsIncModal.format(90), gMotionModal.format(0), x, y); + } + } + + if (insertToolCall) { + if (properties.preloadTool) { + var nextTool = getNextTool(tool.number); + if (nextTool) { + writeBlock("T" + toolFormat.format(nextTool.number)); + } else { + // preload first tool + var section = getSection(0); + var firstToolNumber = section.getTool().number; + if (tool.number != firstToolNumber) { + writeBlock("T" + toolFormat.format(firstToolNumber)); + } + } + } + } + + if (isProbeOperation()) { + if (g68RotationMode != 0) { + error(localize("You cannot probe while G68 Rotation is in effect.")); + return; + } + angularProbingMode = getAngularProbingMode(); + writeBlock(gFormat.format(65), "P" + 9832); // spin the probe on + } + if (isInspectionOperation(currentSection) && (typeof inspectionProcessSectionStart == "function")) { + inspectionProcessSectionStart(); + } + // define subprogram + subprogramDefine(initialPosition, abc, retracted, zIsOutput); +} + +function onDwell(seconds) { + if (seconds > 99999.999) { + warning(localize("Dwelling time is out of range.")); + } + seconds = clamp(0.001, seconds, 99999.999); + writeBlock(gFeedModeModal.format(94), gFormat.format(4), "P" + milliFormat.format(seconds * 1000)); +} + +function onSpindleSpeed(spindleSpeed) { + writeBlock(sOutput.format(spindleSpeed)); +} + +function onCycle() { + writeBlock(gPlaneModal.format(17)); +} + +function getCommonCycle(x, y, z, r, c) { + forceXYZ(); // force xyz on first drill hole of any cycle + if (incrementalMode) { + zOutput.format(c); + return [xOutput.format(x), yOutput.format(y), + "Z" + xyzFormat.format(z - r), + "R" + xyzFormat.format(r - c)]; + } else { + return [xOutput.format(x), yOutput.format(y), + zOutput.format(z), + "R" + xyzFormat.format(r)]; + } +} + +function setCyclePosition(_position) { + switch (gPlaneModal.getCurrent()) { + case 17: // XY + zOutput.format(_position); + break; + case 18: // ZX + yOutput.format(_position); + break; + case 19: // YZ + xOutput.format(_position); + break; + } +} + +/** Convert approach to sign. */ +function approach(value) { + validate((value == "positive") || (value == "negative"), "Invalid approach."); + return (value == "positive") ? 1 : -1; +} + +/** + Determine if angular probing is supported. +*/ +function getAngularProbingMode() { + if (machineConfiguration.isMultiAxisConfiguration()) { + if (machineConfiguration.isMachineCoordinate(2)) { + return (ANGLE_PROBE_USE_CAXIS); + } else { + return (ANGLE_PROBE_NOT_SUPPORTED); + } + } else { + return (ANGLE_PROBE_USE_ROTATION); + } +} + +/** + Output rotation offset based on angular probing cycle. +*/ +function setProbingAngle() { + if ((g68RotationMode == 1) || (g68RotationMode == 2)) { // Rotate coordinate system for Angle Probing + if (angularProbingMode == ANGLE_PROBE_USE_ROTATION) { + gRotationModal.reset(); + gAbsIncModal.reset(); + var xCode = (g68RotationMode == 1) ? "X0" : "X[#185]"; + var yCode = (g68RotationMode == 1) ? "Y0" : "Y[#186]"; + writeBlock(gRotationModal.format(68), gAbsIncModal.format(90), xCode, yCode, "R[#189]"); + g68RotationMode = 3; + } else if (angularProbingMode == ANGLE_PROBE_USE_CAXIS) { + var workOffset = probeOutputWorkOffset ? probeOutputWorkOffset : currentWorkOffset; + if (workOffset > 6) { + error(localize("Angle Probing only supports work offsets 1-6.")); + return; + } + var param = 5200 + workOffset * 20 + 5; + writeBlock("#" + param + "=" + "#189"); + g68RotationMode = 0; + } else { + error(localize("Angular Probing is not supported for this machine configuration.")); + return; + } + } +} + +function protectedProbeMove(_cycle, x, y, z) { + var _x = xOutput.format(x); + var _y = yOutput.format(y); + var _z = zOutput.format(z); + if (_z && z >= getCurrentPosition().z) { + writeBlock(gFormat.format(65), "P" + 9810, _z, getFeed(cycle.feedrate)); // protected positioning move + } + if (_x || _y) { + writeBlock(gFormat.format(65), "P" + 9810, _x, _y, getFeed(highFeedrate)); // protected positioning move + } + if (_z && z < getCurrentPosition().z) { + writeBlock(gFormat.format(65), "P" + 9810, _z, getFeed(cycle.feedrate)); // protected positioning move + } +} + +function onCyclePoint(x, y, z) { + if (isInspectionOperation(currentSection) && (typeof inspectionCycleInspect == "function")) { + inspectionCycleInspect(cycle, x, y, z); + return; + } + if (!isSameDirection(getRotation().forward, new Vector(0, 0, 1))) { + expandCyclePoint(x, y, z); + return; + } + var probeWorkOffsetCode; + if (isProbeOperation()) { + if (!isSameDirection(currentSection.workPlane.forward, new Vector(0, 0, 1)) && (!cycle.probeMode || (cycle.probeMode == 0)) && properties.useDWO) { + error(localize("Your machine does not support the selected probing operation with DWO enabled.")); + return; + } + protectedProbeMove(cycle, x, y, z); + + var workOffset = probeOutputWorkOffset ? probeOutputWorkOffset : currentWorkOffset; + if (workOffset > 99) { + error(localize("Work offset is out of range.")); + return; + } else if (workOffset > 6) { + probeWorkOffsetCode = "154." + probe154Format.format(workOffset - 6); + } else { + probeWorkOffsetCode = workOffset + "."; // G54->G59 + } + } + + var forceCycle = false; + switch (cycleType) { + case "tapping-with-chip-breaking": + case "left-tapping-with-chip-breaking": + case "right-tapping-with-chip-breaking": + forceCycle = true; + if (!isFirstCyclePoint()) { + writeBlock(gCycleModal.format(80)); + gMotionModal.reset(); + } + } + if (forceCycle || isFirstCyclePoint() || isProbeOperation()) { + if (!isProbeOperation()) { + // return to initial Z which is clearance plane and set absolute mode + repositionToCycleClearance(cycle, x, y, z); + } + + var F = cycle.feedrate; + var P = !cycle.dwell ? 0 : clamp(1, cycle.dwell * 1000, 99999999); // in milliseconds + + switch (cycleType) { + case "drilling": + writeBlock( + gRetractModal.format(98), gCycleModal.format(81), + getCommonCycle(x, y, z, cycle.retract, cycle.clearance), + feedOutput.format(F) + ); + break; + case "counter-boring": + if (P > 0) { + writeBlock( + gRetractModal.format(98), gCycleModal.format(82), + getCommonCycle(x, y, z, cycle.retract, cycle.clearance), + "P" + milliFormat.format(P), // not optional + feedOutput.format(F) + ); + } else { + writeBlock( + gRetractModal.format(98), gCycleModal.format(81), + getCommonCycle(x, y, z, cycle.retract, cycle.clearance), + feedOutput.format(F) + ); + } + break; + case "chip-breaking": + if ((cycle.accumulatedDepth < cycle.depth) && (cycle.incrementalDepthReduction > 0)) { + expandCyclePoint(x, y, z); + } else if (cycle.accumulatedDepth < cycle.depth) { + writeBlock( + gRetractModal.format(98), gCycleModal.format(73), + getCommonCycle(x, y, z, cycle.retract, cycle.clearance), + ("Q" + xyzFormat.format(cycle.incrementalDepth)), + ("K" + xyzFormat.format(cycle.accumulatedDepth)), + conditional(P > 0, "P" + milliFormat.format(P)), // optional + feedOutput.format(F) + ); + } else { + writeBlock( + gRetractModal.format(98), gCycleModal.format(73), + getCommonCycle(x, y, z, cycle.retract, cycle.clearance), + (((cycle.incrementalDepthReduction > 0) ? "I" : "Q") + xyzFormat.format(cycle.incrementalDepth)), + conditional(cycle.incrementalDepthReduction > 0, "J" + xyzFormat.format(cycle.incrementalDepthReduction)), + conditional(cycle.incrementalDepthReduction > 0, "K" + xyzFormat.format(cycle.minimumIncrementalDepth)), + conditional(P > 0, "P" + milliFormat.format(P)), // optional + feedOutput.format(F) + ); + } + break; + case "deep-drilling": + writeBlock( + gRetractModal.format(98), gCycleModal.format(83), + getCommonCycle(x, y, z, cycle.retract, cycle.clearance), + (((cycle.incrementalDepthReduction > 0) ? "I" : "Q") + xyzFormat.format(cycle.incrementalDepth)), + conditional(cycle.incrementalDepthReduction > 0, "J" + xyzFormat.format(cycle.incrementalDepthReduction)), + conditional(cycle.incrementalDepthReduction > 0, "K" + xyzFormat.format(cycle.minimumIncrementalDepth)), + conditional(P > 0, "P" + milliFormat.format(P)), // optional + feedOutput.format(F) + ); + break; + case "tapping": + var tappingFPM = tool.getThreadPitch() * rpmFormat.getResultingValue(spindleSpeed); + F = (properties.useG95forTapping ? tool.getThreadPitch() : tappingFPM); + if (properties.useG95forTapping) { + writeBlock(gFeedModeModal.format(95)); + } + writeBlock( + gRetractModal.format(98), gCycleModal.format((tool.type == TOOL_TAP_LEFT_HAND) ? 74 : 84), + getCommonCycle(x, y, z, cycle.retract, cycle.clearance), + pitchOutput.format(F) + ); + forceFeed(); + break; + case "left-tapping": + var tappingFPM = tool.getThreadPitch() * rpmFormat.getResultingValue(spindleSpeed); + F = (properties.useG95forTapping ? tool.getThreadPitch() : tappingFPM); + if (properties.useG95forTapping) { + writeBlock(gFeedModeModal.format(95)); + } + writeBlock( + gRetractModal.format(98), gCycleModal.format(74), + getCommonCycle(x, y, z, cycle.retract, cycle.clearance), + pitchOutput.format(F) + ); + forceFeed(); + break; + case "right-tapping": + var tappingFPM = tool.getThreadPitch() * rpmFormat.getResultingValue(spindleSpeed); + F = (properties.useG95forTapping ? tool.getThreadPitch() : tappingFPM); + if (properties.useG95forTapping) { + writeBlock(gFeedModeModal.format(95)); + } + writeBlock( + gRetractModal.format(98), gCycleModal.format(84), + getCommonCycle(x, y, z, cycle.retract, cycle.clearance), + pitchOutput.format(F) + ); + forceFeed(); + break; + case "tapping-with-chip-breaking": + case "left-tapping-with-chip-breaking": + case "right-tapping-with-chip-breaking": + if (cycle.accumulatedDepth < cycle.depth) { + error(localize("Accumulated pecking depth is not supported for tapping cycles with chip breaking.")); + return; + } else { + var tappingFPM = tool.getThreadPitch() * rpmFormat.getResultingValue(spindleSpeed); + F = (properties.useG95forTapping ? tool.getThreadPitch() : tappingFPM); + if (properties.useG95forTapping) { + writeBlock(gFeedModeModal.format(95)); + } + // Parameter 57 bit 6, REPT RIG TAP, is set to 1 (On) + // On Mill software versions12.09 and above, REPT RIG TAP has been moved from the Parameters to Setting 133 + var u = cycle.stock; + var step = cycle.incrementalDepth; + var first = true; + while (u > cycle.bottom) { + if (step < cycle.minimumIncrementalDepth) { + step = cycle.minimumIncrementalDepth; + } + u -= step; + step -= cycle.incrementalDepthReduction; + gCycleModal.reset(); // required + if ((u - 0.001) <= cycle.bottom) { + u = cycle.bottom; + } + if (first) { + first = false; + writeBlock( + gRetractModal.format(99), gCycleModal.format((tool.type == TOOL_TAP_LEFT_HAND ? 74 : 84)), + getCommonCycle((gPlaneModal.getCurrent() == 19) ? u : x, (gPlaneModal.getCurrent() == 18) ? u : y, (gPlaneModal.getCurrent() == 17) ? u : z, cycle.retract, cycle.clearance), + pitchOutput.format(F) + ); + } else { + var position; + var depth; + switch (gPlaneModal.getCurrent()) { + case 17: + xOutput.reset(); + position = xOutput.format(x); + depth = "Z" + xyzFormat.format(u); + break; + case 18: + zOutput.reset(); + position = zOutput.format(z); + depth = "Y" + xyzFormat.format(u); + break; + case 19: + yOutput.reset(); + position = yOutput.format(y); + depth = "X" + xyzFormat.format(u); + break; + } + writeBlock(conditional(u <= cycle.bottom, gRetractModal.format(98)), position, depth); + } + } + } + forceFeed(); + break; + case "fine-boring": + writeBlock( + gRetractModal.format(98), gCycleModal.format(76), + getCommonCycle(x, y, z, cycle.retract, cycle.clearance), + "P" + milliFormat.format(P), // not optional + "Q" + xyzFormat.format(cycle.shift), + feedOutput.format(F) + ); + forceSpindleSpeed = true; + break; + case "back-boring": + if (P > 0) { + expandCyclePoint(x, y, z); + } else { + 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), gCycleModal.format(77), + getCommonCycle(x - dx, y - dy, z - dz, cycle.bottom, cycle.clearance), + "Q" + xyzFormat.format(cycle.shift), + feedOutput.format(F) + ); + forceSpindleSpeed = true; + } + break; + case "reaming": + writeBlock( + gRetractModal.format(98), gCycleModal.format(85), + getCommonCycle(x, y, z, cycle.retract, cycle.clearance), + feedOutput.format(F) + ); + break; + case "stop-boring": + if (P > 0) { + expandCyclePoint(x, y, z); + } else { + writeBlock( + gRetractModal.format(98), gCycleModal.format(86), + getCommonCycle(x, y, z, cycle.retract, cycle.clearance), + feedOutput.format(F) + ); + forceSpindleSpeed = true; + } + break; + case "manual-boring": + writeBlock( + gRetractModal.format(98), gCycleModal.format(88), + getCommonCycle(x, y, z, cycle.retract, cycle.clearance), + "P" + milliFormat.format(P), // not optional + feedOutput.format(F) + ); + break; + case "boring": + writeBlock( + gRetractModal.format(98), gCycleModal.format(89), + getCommonCycle(x, y, z, cycle.retract, cycle.clearance), + "P" + milliFormat.format(P), // not optional + feedOutput.format(F) + ); + break; + + case "probing-x": + protectedProbeMove(cycle, x, y, z - cycle.depth); + writeBlock( + gFormat.format(65), "P" + 9811, + "X" + xyzFormat.format(x + approach(cycle.approach1) * (cycle.probeClearance + tool.diameter / 2)), + "Q" + xyzFormat.format(cycle.probeOvertravel), + getProbingArguments(cycle, probeWorkOffsetCode) + ); + break; + case "probing-y": + protectedProbeMove(cycle, x, y, z - cycle.depth); + writeBlock( + gFormat.format(65), "P" + 9811, + "Y" + xyzFormat.format(y + approach(cycle.approach1) * (cycle.probeClearance + tool.diameter / 2)), + "Q" + xyzFormat.format(cycle.probeOvertravel), + getProbingArguments(cycle, probeWorkOffsetCode) + ); + break; + case "probing-z": + protectedProbeMove(cycle, x, y, Math.min(z - cycle.depth + cycle.probeClearance, cycle.retract)); + writeBlock( + gFormat.format(65), "P" + 9811, + "Z" + xyzFormat.format(z - cycle.depth), + "Q" + xyzFormat.format(cycle.probeOvertravel), + getProbingArguments(cycle, probeWorkOffsetCode) + ); + break; + case "probing-x-wall": + protectedProbeMove(cycle, x, y, z); + writeBlock( + gFormat.format(65), "P" + 9812, + "X" + xyzFormat.format(cycle.width1), + zOutput.format(z - cycle.depth), + "Q" + xyzFormat.format(cycle.probeOvertravel), + "R" + xyzFormat.format(cycle.probeClearance), + getProbingArguments(cycle, probeWorkOffsetCode) + ); + break; + case "probing-y-wall": + protectedProbeMove(cycle, x, y, z); + writeBlock( + gFormat.format(65), "P" + 9812, + "Y" + xyzFormat.format(cycle.width1), + zOutput.format(z - cycle.depth), + "Q" + xyzFormat.format(cycle.probeOvertravel), + "R" + xyzFormat.format(cycle.probeClearance), + getProbingArguments(cycle, probeWorkOffsetCode) + ); + break; + case "probing-x-channel": + protectedProbeMove(cycle, x, y, z - cycle.depth); + writeBlock( + gFormat.format(65), "P" + 9812, + "X" + xyzFormat.format(cycle.width1), + "Q" + xyzFormat.format(cycle.probeOvertravel), + // not required "R" + xyzFormat.format(cycle.probeClearance), + getProbingArguments(cycle, probeWorkOffsetCode) + ); + break; + case "probing-x-channel-with-island": + protectedProbeMove(cycle, x, y, z); + writeBlock( + gFormat.format(65), "P" + 9812, + "X" + xyzFormat.format(cycle.width1), + zOutput.format(z - cycle.depth), + "Q" + xyzFormat.format(cycle.probeOvertravel), + "R" + xyzFormat.format(-cycle.probeClearance), + getProbingArguments(cycle, probeWorkOffsetCode) + ); + break; + case "probing-y-channel": + protectedProbeMove(cycle, x, y, z - cycle.depth); + writeBlock( + gFormat.format(65), "P" + 9812, + "Y" + xyzFormat.format(cycle.width1), + "Q" + xyzFormat.format(cycle.probeOvertravel), + // not required "R" + xyzFormat.format(cycle.probeClearance), + getProbingArguments(cycle, probeWorkOffsetCode) + ); + break; + case "probing-y-channel-with-island": + protectedProbeMove(cycle, x, y, z); + writeBlock( + gFormat.format(65), "P" + 9812, + "Y" + xyzFormat.format(cycle.width1), + zOutput.format(z - cycle.depth), + "Q" + xyzFormat.format(cycle.probeOvertravel), + "R" + xyzFormat.format(-cycle.probeClearance), + getProbingArguments(cycle, probeWorkOffsetCode) + ); + break; + case "probing-xy-circular-boss": + protectedProbeMove(cycle, x, y, z); + writeBlock( + gFormat.format(65), "P" + 9814, + "D" + xyzFormat.format(cycle.width1), + "Z" + xyzFormat.format(z - cycle.depth), + "Q" + xyzFormat.format(cycle.probeOvertravel), + "R" + xyzFormat.format(cycle.probeClearance), + getProbingArguments(cycle, probeWorkOffsetCode) + ); + break; + case "probing-xy-circular-hole": + protectedProbeMove(cycle, x, y, z - cycle.depth); + writeBlock( + gFormat.format(65), "P" + 9814, + "D" + xyzFormat.format(cycle.width1), + "Q" + xyzFormat.format(cycle.probeOvertravel), + // not required "R" + xyzFormat.format(cycle.probeClearance), + getProbingArguments(cycle, probeWorkOffsetCode) + ); + break; + case "probing-xy-circular-hole-with-island": + protectedProbeMove(cycle, x, y, z); + writeBlock( + gFormat.format(65), "P" + 9814, + "Z" + xyzFormat.format(z - cycle.depth), + "D" + xyzFormat.format(cycle.width1), + "Q" + xyzFormat.format(cycle.probeOvertravel), + "R" + xyzFormat.format(-cycle.probeClearance), + getProbingArguments(cycle, probeWorkOffsetCode) + ); + break; + case "probing-xy-rectangular-hole": + protectedProbeMove(cycle, x, y, z - cycle.depth); + writeBlock( + gFormat.format(65), "P" + 9812, + "X" + xyzFormat.format(cycle.width1), + "Q" + xyzFormat.format(cycle.probeOvertravel), + // not required "R" + xyzFormat.format(-cycle.probeClearance), + getProbingArguments(cycle, probeWorkOffsetCode) + ); + writeBlock( + gFormat.format(65), "P" + 9812, + "Y" + xyzFormat.format(cycle.width2), + "Q" + xyzFormat.format(cycle.probeOvertravel), + // not required "R" + xyzFormat.format(-cycle.probeClearance), + getProbingArguments(cycle, probeWorkOffsetCode) + ); + break; + case "probing-xy-rectangular-boss": + protectedProbeMove(cycle, x, y, z); + writeBlock( + gFormat.format(65), "P" + 9812, + "Z" + xyzFormat.format(z - cycle.depth), + "X" + xyzFormat.format(cycle.width1), + "R" + xyzFormat.format(cycle.probeClearance), + "Q" + xyzFormat.format(cycle.probeOvertravel), + getProbingArguments(cycle, probeWorkOffsetCode) + ); + writeBlock( + gFormat.format(65), "P" + 9812, + "Z" + xyzFormat.format(z - cycle.depth), + "Y" + xyzFormat.format(cycle.width2), + "R" + xyzFormat.format(cycle.probeClearance), + "Q" + xyzFormat.format(cycle.probeOvertravel), + getProbingArguments(cycle, probeWorkOffsetCode) + ); + break; + case "probing-xy-rectangular-hole-with-island": + protectedProbeMove(cycle, x, y, z); + writeBlock( + gFormat.format(65), "P" + 9812, + "Z" + xyzFormat.format(z - cycle.depth), + "X" + xyzFormat.format(cycle.width1), + "Q" + xyzFormat.format(cycle.probeOvertravel), + "R" + xyzFormat.format(-cycle.probeClearance), + getProbingArguments(cycle, probeWorkOffsetCode) + ); + writeBlock( + gFormat.format(65), "P" + 9812, + "Z" + xyzFormat.format(z - cycle.depth), + "Y" + xyzFormat.format(cycle.width2), + "Q" + xyzFormat.format(cycle.probeOvertravel), + "R" + xyzFormat.format(-cycle.probeClearance), + getProbingArguments(cycle, probeWorkOffsetCode) + ); + break; + + case "probing-xy-inner-corner": + var cornerX = x + approach(cycle.approach1) * (cycle.probeClearance + tool.diameter / 2); + var cornerY = y + approach(cycle.approach2) * (cycle.probeClearance + tool.diameter / 2); + var cornerI = 0; + var cornerJ = 0; + if (cycle.probeSpacing && (cycle.probeSpacing != 0)) { + cornerI = cycle.probeSpacing; + cornerJ = cycle.probeSpacing; + } + if ((cornerI != 0) && (cornerJ != 0)) { + g68RotationMode = 2; + } + protectedProbeMove(cycle, x, y, z - cycle.depth); + writeBlock( + gFormat.format(65), "P" + 9815, xOutput.format(cornerX), yOutput.format(cornerY), + conditional(cornerI != 0, "I" + xyzFormat.format(cornerI)), + conditional(cornerJ != 0, "J" + xyzFormat.format(cornerJ)), + "Q" + xyzFormat.format(cycle.probeOvertravel), + conditional((g68RotationMode == 0) || (angularProbingMode == ANGLE_PROBE_USE_CAXIS), getProbingArguments(cycle, probeWorkOffsetCode)) + ); + break; + case "probing-xy-outer-corner": + var cornerX = x + approach(cycle.approach1) * (cycle.probeClearance + tool.diameter / 2); + var cornerY = y + approach(cycle.approach2) * (cycle.probeClearance + tool.diameter / 2); + var cornerI = 0; + var cornerJ = 0; + if (cycle.probeSpacing && (cycle.probeSpacing != 0)) { + cornerI = cycle.probeSpacing; + cornerJ = cycle.probeSpacing; + } + if ((cornerI != 0) && (cornerJ != 0)) { + g68RotationMode = 2; + } + protectedProbeMove(cycle, x, y, z - cycle.depth); + writeBlock( + gFormat.format(65), "P" + 9816, xOutput.format(cornerX), yOutput.format(cornerY), + conditional(cornerI != 0, "I" + xyzFormat.format(cornerI)), + conditional(cornerJ != 0, "J" + xyzFormat.format(cornerJ)), + "Q" + xyzFormat.format(cycle.probeOvertravel), + conditional((g68RotationMode == 0) || (angularProbingMode == ANGLE_PROBE_USE_CAXIS), getProbingArguments(cycle, probeWorkOffsetCode)) + ); + break; + case "probing-x-plane-angle": + protectedProbeMove(cycle, x, y, z - cycle.depth); + writeBlock( + gFormat.format(65), "P" + 9843, + "X" + xyzFormat.format(x + approach(cycle.approach1) * (cycle.probeClearance + tool.diameter / 2)), + "D" + xyzFormat.format(cycle.probeSpacing), + "Q" + xyzFormat.format(cycle.probeOvertravel), + getProbingArguments(cycle, false) + ); + g68RotationMode = 1; + break; + case "probing-y-plane-angle": + protectedProbeMove(cycle, x, y, z - cycle.depth); + writeBlock( + gFormat.format(65), "P" + 9843, + "Y" + xyzFormat.format(y + approach(cycle.approach1) * (cycle.probeClearance + tool.diameter / 2)), + "D" + xyzFormat.format(cycle.probeSpacing), + "Q" + xyzFormat.format(cycle.probeOvertravel), + getProbingArguments(cycle, false) + ); + g68RotationMode = 1; + break; + + default: + expandCyclePoint(x, y, z); + } + + // place cycle operation in subprogram + if (cycleSubprogramIsActive) { + if (forceCycle || cycleExpanded || isProbeOperation()) { + cycleSubprogramIsActive = false; + } else { + // call subprogram + writeBlock(mFormat.format(97), "P" + nFormat.format(currentSubprogram)); + subprogramStart(new Vector(x, y, z), new Vector(0, 0, 0), false); + } + } + if (incrementalMode) { // set current position to clearance height + setCyclePosition(cycle.clearance); + } + + // 2nd through nth cycle point + } else { + if (cycleExpanded) { + expandCyclePoint(x, y, z); + } else { + var _x; + var _y; + var _z; + if (!xyzFormat.areDifferent(x, xOutput.getCurrent()) && + !xyzFormat.areDifferent(y, yOutput.getCurrent()) && + !xyzFormat.areDifferent(z, zOutput.getCurrent())) { + switch (gPlaneModal.getCurrent()) { + case 17: // XY + xOutput.reset(); // at least one axis is required + break; + case 18: // ZX + zOutput.reset(); // at least one axis is required + break; + case 19: // YZ + yOutput.reset(); // at least one axis is required + break; + } + } + if (incrementalMode) { // set current position to retract height + setCyclePosition(cycle.retract); + } + writeBlock(xOutput.format(x), yOutput.format(y), zOutput.format(z)); + if (incrementalMode) { // set current position to clearance height + setCyclePosition(cycle.clearance); + } + } + } +} + +function getProbingArguments(cycle, probeWorkOffsetCode) { + var probeWCS = hasParameter("operation-strategy") && (getParameter("operation-strategy") == "probe"); + return [ + (cycle.angleAskewAction == "stop-message" ? "B" + xyzFormat.format(cycle.toleranceAngle ? cycle.toleranceAngle : 0) : undefined), + ((cycle.updateToolWear && cycle.toolWearErrorCorrection < 100) ? "F" + xyzFormat.format(cycle.toolWearErrorCorrection ? cycle.toolWearErrorCorrection / 100 : 100) : undefined), + (cycle.wrongSizeAction == "stop-message" ? "H" + xyzFormat.format(cycle.toleranceSize ? cycle.toleranceSize : 0) : undefined), + (cycle.outOfPositionAction == "stop-message" ? "M" + xyzFormat.format(cycle.tolerancePosition ? cycle.tolerancePosition : 0) : undefined), + ((cycle.updateToolWear && cycleType == "probing-z") ? "T" + xyzFormat.format(cycle.toolLengthOffset) : undefined), + ((cycle.updateToolWear && cycleType !== "probing-z") ? "T" + xyzFormat.format(cycle.toolDiameterOffset) : undefined), + (cycle.updateToolWear ? "V" + xyzFormat.format(cycle.toolWearUpdateThreshold ? cycle.toolWearUpdateThreshold : 0) : undefined), + (cycle.printResults ? "W" + xyzFormat.format(1 + cycle.incrementComponent) : undefined), // 1 for advance feature, 2 for reset feature count and advance component number. first reported result in a program should use W2. + conditional(probeWorkOffsetCode && probeWCS, "S" + probeWorkOffsetCode) + ]; +} + +function onCycleEnd() { + if (isProbeOperation()) { + zOutput.reset(); + gMotionModal.reset(); + writeBlock(gFormat.format(65), "P" + 9810, zOutput.format(cycle.retract)); // protected retract move + } else { + if (cycleSubprogramIsActive) { + subprogramEnd(); + cycleSubprogramIsActive = false; + } + if (!cycleExpanded) { + writeBlock(gCycleModal.format(80), conditional(properties.useG95forTapping, gFeedModeModal.format(94))); + 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; + } + if (!properties.useG0 && (((x ? 1 : 0) + (y ? 1 : 0) + (z ? 1 : 0)) > 1)) { + // axes are not synchronized + writeBlock(gMotionModal.format(1), x, y, z, getFeed(highFeedrate)); + } else { + writeBlock(gMotionModal.format(0), x, y, z); + forceFeed(); + } + } +} + +function onLinear(_x, _y, _z, feed) { + if (pendingRadiusCompensation >= 0) { + // ensure that we end at desired position when compensation is turned off + xOutput.reset(); + yOutput.reset(); + } + var x = xOutput.format(_x); + var y = yOutput.format(_y); + var z = zOutput.format(_z); + var f = getFeed(feed); + if (x || y || z) { + if (pendingRadiusCompensation >= 0) { + pendingRadiusCompensation = -1; + var d = tool.diameterOffset; + if ((d > 200 && d < 1000) || d > 9999) { + warning(localize("Diameter offset out of range.")); + } + writeBlock(gPlaneModal.format(17)); + switch (radiusCompensation) { + case RADIUS_COMPENSATION_LEFT: + dOutput.reset(); + writeBlock(gMotionModal.format(1), gFormat.format(41), x, y, z, dOutput.format(d), f); + break; + case RADIUS_COMPENSATION_RIGHT: + dOutput.reset(); + writeBlock(gMotionModal.format(1), gFormat.format(42), x, y, z, dOutput.format(d), f); + break; + default: + writeBlock(gMotionModal.format(1), gFormat.format(40), x, y, z, f); + } + } else { + writeBlock(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(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); + if (true) { // required for multi-axis + // axes are not synchronized + writeBlock(gMotionModal.format(1), x, y, z, a, b, c, getFeed(highFeedrate)); + } else { + writeBlock(gMotionModal.format(0), x, y, z, a, b, c); + 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) && !properties.useTCPC) { + f = getMultiaxisFeed(_x, _y, _z, _a, _b, _c, feed); + if (properties.useDPMFeeds) { + f.frn = feedOutput.format(f.frn); + } else { + f.frn = inverseTimeOutput.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 + feedOutput.reset(); // force feed on next line + } else { + writeBlock(gFeedModeModal.format(f.fmode), gMotionModal.format(1), f.frn); + } + } +} + +// Start of multi-axis feedrate logic +/***** Be sure to add 'useInverseTime' to post properties if necessary. *****/ +/***** 'inverseTimeOutput' should be defined if Inverse Time feedrates are supported. *****/ +/***** '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 = 9999; // maximum value to output for Inverse Time feeds +var maxDPM = 9999.99; // maximum value to output for DPM feeds +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 (!properties.useDPMFeeds) { // 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; // !getOptimizedMode() && (forceOptimized == undefined); // set to false for rotary heads + 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 properties. *****/ +var performRewinds = false; // enables the onRewindMachine logic +var safeRetractFeed = (unit == IN) ? 20 : 500; +var safePlungeFeed = (unit == IN) ? 10 : 250; +var stockAllowance = (unit == IN) ? 0.1 : 2.5; + +/** Allow user to override the onRewind logic. */ +function onRewindMachineEntry(_a, _b, _c) { + return false; +} + +/** Retract to safe position before indexing rotaries. */ +function moveToSafeRetractPosition(isRetracted) { + if (properties.useG28) { + if (!isRetracted) { + writeRetract(Z); + } + if (false) { + writeRetract(X, Y); + } + } else { + if (!isRetracted) { + writeRetract(Z); + } + if (false) { // e.g. when machining very big parts + writeRetract(X, Y); + } + } +} + +/** Return from safe position after indexing rotaries. */ +function returnFromSafeRetractPosition(position) { + forceXYZ(); + xOutput.reset(); + yOutput.reset(); + zOutput.disable(); + onExpandedRapid(position.x, position.y, position.z); + zOutput.enable(); + onExpandedRapid(position.x, position.y, position.z); +} + +/** Determine if a point is on the correct side of a box side. */ +function isPointInBoxSide(point, side) { + var inBox = false; + switch (side.side) { + case "-X": + if (point.x >= side.distance) { + inBox = true; + } + break; + case "-Y": + if (point.y >= side.distance) { + inBox = true; + } + break; + case "-Z": + if (point.z >= side.distance) { + inBox = true; + } + break; + case "X": + if (point.x <= side.distance) { + inBox = true; + } + break; + case "Y": + if (point.y <= side.distance) { + inBox = true; + } + break; + case "Z": + if (point.z <= side.distance) { + inBox = true; + } + break; + } + return inBox; +} + +/** Intersect a point-vector with a plane. */ +function intersectPlane(point, direction, plane) { + var normal = new Vector(plane.x, plane.y, plane.z); + var cosa = Vector.dot(normal, direction); + if (Math.abs(cosa) <= 1.0e-6) { + return undefined; + } + var distance = (Vector.dot(normal, point) - plane.distance) / cosa; + var intersection = Vector.diff(point, Vector.product(direction, distance)); + + if (!isSameDirection(Vector.diff(intersection, point).getNormalized(), direction)) { + return undefined; + } + return intersection; +} + +/** Intersect the point-vector with the stock box. */ +function intersectStock(point, direction) { + var stock = getWorkpiece(); + var sides = new Array( + {x:1, y:0, z:0, distance:stock.lower.x, side:"-X"}, + {x:0, y:1, z:0, distance:stock.lower.y, side:"-Y"}, + {x:0, y:0, z:1, distance:stock.lower.z, side:"-Z"}, + {x:1, y:0, z:0, distance:stock.upper.x, side:"X"}, + {x:0, y:1, z:0, distance:stock.upper.y, side:"Y"}, + {x:0, y:0, z:1, distance:stock.upper.z, side:"Z"} + ); + var intersection = undefined; + var currentDistance = 999999.0; + var localExpansion = -stockAllowance; + for (var i = 0; i < sides.length; ++i) { + if (i == 3) { + localExpansion = -localExpansion; + } + if (isPointInBoxSide(point, sides[i])) { // only consider points within stock box + var location = intersectPlane(point, direction, sides[i]); + if (location != undefined) { + if ((Vector.diff(point, location).length < currentDistance) || currentDistance == 0) { + intersection = location; + currentDistance = Vector.diff(point, location).length; + } + } + } + } + return intersection; +} + +/** 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) && properties.safeRetractDistance) { + retractPos = Vector.sum(retractPos, Vector.product(currentDirection, properties.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); + + // Cancel TCP so that tool doesn't follow tables + writeBlock(gFormat.format(49), formatComment("TCPC OFF")); + + //Position to safe machine position for rewinding axes + moveToSafeRetractPosition(false); + + // Rotate axes to new position above reentry position + xOutput.disable(); + yOutput.disable(); + zOutput.disable(); + onRapid5D(position.x, position.y, position.z, _a, _b, _c); + xOutput.enable(); + yOutput.enable(); + zOutput.enable(); + + // Reinstate TCP + writeBlock(gFormat.format(234), hFormat.format(tool.lengthOffset), formatComment("TCPC ON")); + + // 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 (isSpiral()) { + var startRadius = getCircularStartRadius(); + var endRadius = getCircularRadius(); + var dr = Math.abs(endRadius - startRadius); + if (dr > maximumCircularRadiiDifference) { // maximum limit + linearize(tolerance); // or alternatively use other G-codes for spiral motion + return; + } + } + + if (pendingRadiusCompensation >= 0) { + error(localize("Radius compensation cannot be activated/deactivated for a circular move.")); + return; + } + + var start = getCurrentPosition(); + + if (isFullCircle()) { + if (properties.useRadius || isHelical()) { // radius mode does not support full arcs + linearize(tolerance); + return; + } + switch (getCircularPlane()) { + case PLANE_XY: + writeBlock(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(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(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 (!properties.useRadius) { + switch (getCircularPlane()) { + case PLANE_XY: + writeBlock(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(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(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(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(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(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; +var isOptionalCoolant = false; + +function setCoolant(coolant) { + var coolantCodes = getCoolantCodes(coolant); + if (Array.isArray(coolantCodes)) { + if (singleLineCoolant) { + writeBlock((isOptionalCoolant ? "/" : ""), coolantCodes.join(getWordSeparator())); + } else { + for (var c in coolantCodes) { + writeBlock((isOptionalCoolant ? "/" : ""), coolantCodes[c]); + } + } + return undefined; + } + return coolantCodes; +} + +function getCoolantCodes(coolant) { + isOptionalCoolant = false; + 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) { + if (properties.safeStartAllOperations) { + isOptionalCoolant = true; + } else { + return undefined; // coolant is already active + } + } + if ((coolant != COOLANT_OFF) && (currentCoolantMode != COOLANT_OFF) && !isOptionalCoolant) { + 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_OPTIONAL_STOP: + writeBlock(mFormat.format(1)); + forceSpindleSpeed = true; + return; + case COMMAND_COOLANT_ON: + setCoolant(COOLANT_FLOOD); + return; + case COMMAND_COOLANT_OFF: + setCoolant(COOLANT_OFF); + return; + case COMMAND_START_SPINDLE: + onCommand(tool.clockwise ? COMMAND_SPINDLE_CLOCKWISE : COMMAND_SPINDLE_COUNTERCLOCKWISE); + return; + case COMMAND_LOCK_MULTI_AXIS: + if (machineConfiguration.isMultiAxisConfiguration() && (machineConfiguration.getNumberOfAxes() >= 4)) { + writeBlock(operationNeedsSafeWorkPlane ? "/" : "", mFormat.format(10)); // lock 4th-axis motion + if (machineConfiguration.getNumberOfAxes() == 5) { + writeBlock(operationNeedsSafeWorkPlane ? "/" : "", mFormat.format(12)); // lock 5th-axis motion + } + } + return; + case COMMAND_UNLOCK_MULTI_AXIS: + if (machineConfiguration.isMultiAxisConfiguration() && (machineConfiguration.getNumberOfAxes() >= 4)) { + writeBlock(operationNeedsSafeWorkPlane ? "/" : "", mFormat.format(11)); // unlock 4th-axis motion + if (machineConfiguration.getNumberOfAxes() == 5) { + writeBlock(operationNeedsSafeWorkPlane ? "/" : "", mFormat.format(13)); // unlock 5th-axis motion + } + } + return; + case COMMAND_BREAK_CONTROL: + if (!toolChecked) { // avoid duplicate COMMAND_BREAK_CONTROL + onCommand(COMMAND_STOP_SPINDLE); + onCommand(COMMAND_COOLANT_OFF); + + var retract = false; + if (currentSection.isMultiAxis()) { + if (getCurrentDirection().length != 0) { + retract = true; + } + } else if ((currentWorkPlaneABC != undefined) && (currentWorkPlaneABC.length != 0)) { + retract = true; + } + if (retract) { // move to safe position + moveToSafeRetractPosition(false); + } + + if (activeG254) { // cancel DWO + writeBlock(gFormat.format(255)); + activeG254 = false; + } + + if (retract) { // position rotary axes at 0-degrees + writeBlock( + gMotionModal.format(0), + conditional(machineConfiguration.isMachineCoordinate(0), "A" + abcFormat.format(0)), + conditional(machineConfiguration.isMachineCoordinate(1), "B" + abcFormat.format(0)), + conditional(machineConfiguration.isMachineCoordinate(2), "C" + abcFormat.format(0)) + ); + } + + writeBlock( + gFormat.format(65), + "P" + 9853, + "T" + toolFormat.format(tool.number), + "B" + xyzFormat.format(0), + "H" + xyzFormat.format(properties.toolBreakageTolerance) + ); + toolChecked = true; + } + return; + case COMMAND_TOOL_MEASURE: + measureTool = true; + return; + case COMMAND_START_CHIP_TRANSPORT: + writeBlock(mFormat.format(31)); + return; + case COMMAND_STOP_CHIP_TRANSPORT: + writeBlock(mFormat.format(33)); + return; + case COMMAND_PROBE_ON: + return; + case COMMAND_PROBE_OFF: + return; + } + + var stringId = getCommandStringId(command); + var mcode = mapCommand[stringId]; + if (mcode != undefined) { + writeBlock(mFormat.format(mcode)); + } else { + onUnsupportedCommand(command); + } +} + +var toolChecked = false; // specifies that the tool has been checked with the probe + +function onSectionEnd() { + if (typeof inspectionProcessSectionEnd == "function") { + inspectionProcessSectionEnd(); + } + if (!isLastSection() && (getNextSection().getTool().coolant != tool.coolant)) { + setCoolant(COOLANT_OFF); + } + if ((((getCurrentSectionId() + 1) >= getNumberOfSections()) || + (tool.number != getNextSection().getTool().number)) && + tool.breakControl) { + onCommand(COMMAND_BREAK_CONTROL); + } else { + toolChecked = false; + } + + if (true) { + if (isRedirecting()) { + if (firstPattern) { + var finalPosition = getFramePosition(currentSection.getFinalPosition()); + var abc; + if (currentSection.isMultiAxis() && machineConfiguration.isMultiAxisConfiguration()) { + abc = currentSection.getFinalToolAxisABC(); + } else { + abc = currentWorkPlaneABC; + } + if (abc == undefined) { + abc = new Vector(0, 0, 0); + } + setAbsoluteMode(finalPosition, abc); + subprogramEnd(); + } + } + } + forceAny(); + + if (currentSection.isMultiAxis() && (currentSection.getOptimizedTCPMode() == 0)) { + writeBlock(gFormat.format(49), "(TCPC OFF)"); + } + if (isProbeOperation()) { + writeBlock(gFormat.format(65), "P" + 9833); // spin the probe off + setProbingAngle(); // define rotation of part + } + + operationNeedsSafeStart = false; // reset for next section + operationNeedsSafeToolCall = false; + operationNeedsSafeWorkOffset = false; + operationNeedsSafeWorkPlane = false; + operationNeedsSafeCoolant = false; +} + +/** Output block to do safe retract and/or move to home position. */ +function writeRetract() { + // initialize routine + var _xyzMoved = new Array(false, false, false); + var _useG28 = properties.useG28; // can be either true or false + + // check syntax of call + if (arguments.length == 0) { + error(localize("No axis specified for writeRetract().")); + return; + } + for (var i = 0; i < arguments.length; ++i) { + if ((arguments[i] < 0) || (arguments[i] > 2)) { + error(localize("Bad axis specified for writeRetract().")); + return; + } + if (_xyzMoved[arguments[i]]) { + error(localize("Cannot retract the same axis twice in one line")); + return; + } + _xyzMoved[arguments[i]] = true; + } + + // special conditions + if (_useG28 && _xyzMoved[2] && (_xyzMoved[0] || _xyzMoved[1])) { // XY don't use G28 + error(localize("You cannot move home in XY & Z in the same block.")); + return; + } + if (_xyzMoved[0] || _xyzMoved[1]) { + _useG28 = false; + } + + // define home positions + var _xHome; + var _yHome; + var _zHome; + if (_useG28) { + _xHome = 0; + _yHome = 0; + _zHome = 0; + } else { + if (properties.homePositionCenter && + hasParameter("part-upper-x") && hasParameter("part-lower-x")) { + _xHome = (getParameter("part-upper-x") + getParameter("part-lower-x")) / 2; + } else { + _xHome = machineConfiguration.hasHomePositionX() ? machineConfiguration.getHomePositionX() : 0; + } + _yHome = machineConfiguration.hasHomePositionY() ? machineConfiguration.getHomePositionY() : 0; + _zHome = machineConfiguration.getRetractPlane(); + } + + // format home positions + var words = []; // store all retracted axes in an array + for (var i = 0; i < arguments.length; ++i) { + // define the axes to move + switch (arguments[i]) { + case X: + // special conditions + if (properties.homePositionCenter) { // output X in standard block by itself if centering + writeBlock(gMotionModal.format(0), xOutput.format(_xHome)); + _xyzMoved[0] = false; + break; + } + words.push("X" + xyzFormat.format(_xHome)); + break; + case Y: + words.push("Y" + xyzFormat.format(_yHome)); + break; + case Z: + words.push("Z" + xyzFormat.format(_zHome)); + retracted = true; + break; + } + } + + // output move to home + if (words.length > 0) { + if (_useG28) { + gAbsIncModal.reset(); + writeBlock(gFormat.format(28), gAbsIncModal.format(91), words); + writeBlock(gAbsIncModal.format(90)); + } else { + gMotionModal.reset(); + writeBlock(gAbsIncModal.format(90), gFormat.format(53), gMotionModal.format(0), words); + } + + // force any axes that move to home on next block + if (_xyzMoved[0]) { + xOutput.reset(); + } + if (_xyzMoved[1]) { + yOutput.reset(); + } + if (_xyzMoved[2]) { + zOutput.reset(); + } + } +} + +function onClose() { + writeln(""); + + optionalSection = false; + + onCommand(COMMAND_STOP_SPINDLE); + onCommand(COMMAND_COOLANT_OFF); + + // retract + writeRetract(Z); + + writeRetract(X, Y); + + if (activeG254) { + writeBlock(gFormat.format(255)); // cancel DWO + activeG254 = false; + } + + // MAY NEED CHANGE HOMING ORDER TO ROTARY THEN LINEAR FOR NON-UMC MACHINES + + // Unwind Rotary table at end + if (hasC) { + writeBlock(gFormat.format(28), gAbsIncModal.format(91), "C" + abcFormat.format(0)); + writeBlock(gAbsIncModal.format(90)); + } else if (hasB) { + writeBlock(gFormat.format(28), gAbsIncModal.format(91), "B" + abcFormat.format(0)); + writeBlock(gAbsIncModal.format(90)); + } else if (hasA) { + writeBlock(gFormat.format(28), gAbsIncModal.format(91), "A" + abcFormat.format(0)); + writeBlock(gAbsIncModal.format(90)); + } + + if (hasA || hasB || hasC) { + var abc = new Vector(0, 0, 0); + 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)) + ); + } + + writeBlock(gRotationModal.format(69)); + onImpliedCommand(COMMAND_END); + onImpliedCommand(COMMAND_STOP_SPINDLE); + writeBlock(mFormat.format(30)); // stop program, spindle stop, coolant off + if (subprograms.length > 0) { + writeln(""); + write(subprograms); + } + writeln(""); + writeln("%"); +} + +/* +keywords += (keywords ? " MODEL_IMAGE" : "MODEL_IMAGE"); + +function onTerminate() { + var outputPath = getOutputPath(); + var programFilename = FileSystem.getFilename(outputPath); + var programSize = FileSystem.getFileSize(outputPath); + var postPath = findFile("setup-sheet-excel-2007.cps"); + var intermediatePath = getIntermediatePath(); + var a = "--property unit " + ((unit == IN) ? "0" : "1"); // use 0 for inch and 1 for mm + if (programName) { + a += " --property programName \"'" + programName + "'\""; + } + if (programComment) { + a += " --property programComment \"'" + programComment + "'\""; + } + a += " --property programFilename \"'" + programFilename + "'\""; + a += " --property programSize \"" + programSize + "\""; + a += " --noeditor --log temp.log \"" + postPath + "\" \"" + intermediatePath + "\" \"" + FileSystem.replaceExtension(outputPath, "xlsx") + "\""; + execute(getPostProcessorPath(), a, false, ""); + executeNoWait("excel", "\"" + FileSystem.replaceExtension(outputPath, "xlsx") + "\"", false, ""); +} +*/ diff --git a/Haas_ST-20Y/haas st-20y.cps b/Haas_ST-20Y/haas st-20y.cps new file mode 100644 index 0000000..1fa98d8 --- /dev/null +++ b/Haas_ST-20Y/haas st-20y.cps @@ -0,0 +1,3738 @@ +/** + Copyright (C) 2012-2019 by Autodesk, Inc. + All rights reserved. + + HAAS Lathe post processor configuration. + + $Revision: 42650 50adfb6cb80e478bb4605cdfcfd268829708e272 $ + $Date: 2020-02-13 07:31:21 $ + + FORKID {14D60AD3-4366-49dc-939C-4DB5EA48FF68} +*/ + +description = "HAAS ST-20Y"; + +var gotYAxis = true; +var yAxisMinimum = toPreciseUnit(gotYAxis ? -50.8 : 0, MM); // specifies the minimum range for the Y-axis +var yAxisMaximum = toPreciseUnit(gotYAxis ? 50.8 : 0, MM); // specifies the maximum range for the Y-axis +var xAxisMinimum = toPreciseUnit(0, MM); // specifies the maximum range for the X-axis (RADIUS MODE VALUE) +var gotBAxis = false; // B-axis always requires customization to match the machine specific functions for doing rotations +var gotMultiTurret = false; // specifies if the machine has several turrets + +var gotDoorControl = false; + +// >>>>> INCLUDED FROM ../common/haas lathe.cps +/////////////////////////////////////////////////////////////////////////////// +// MANUAL NC COMMANDS +// +// The following ACTION commands are supported by this post. +// +// partEject - Manually eject the part +// usePolarMode - Force Polar mode for next operation +// +/////////////////////////////////////////////////////////////////////////////// + +if (!description) { + description = "HAAS Lathe"; +} +vendor = "Haas Automation"; +vendorUrl = "https://www.haascnc.com"; +legal = "Copyright (C) 2012-2020 by Autodesk, Inc."; +certificationLevel = 2; +minimumRevision = 40783; + +if (!longDescription) { + longDescription = subst("Preconfigured %1 post with support for mill-turn. You can force G112 mode for a specific operation by using Manual NC Action with keyword 'usepolarmode'.", description); +} + +extension = "nc"; +programNameIsInteger = true; +setCodePage("ascii"); + +capabilities = CAPABILITY_MILLING | 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(120); // reduced sweep due to G112 support +allowHelicalMoves = true; +allowedCircularPlanes = undefined; // allow any circular motion +allowSpiralMoves = false; +highFeedrate = (unit == IN) ? 470 : 12000; + +// user-defined properties +properties = { + writeMachine: false, // write machine + writeTools: false, // writes the tools + writeVersion: false, // include version info + // preloadTool: false, // preloads next tool on tool change if any + showSequenceNumbers: false, // show sequence numbers + sequenceNumberStart: 10, // first sequence number + sequenceNumberIncrement: 1, // increment for sequence numbers + optionalStop: true, // optional stop + separateWordsWithSpace: true, // specifies that the words should be separated with a white space + useRadius: false, // specifies that arcs should be output using the radius (R word) instead of the I, J, and K words. + maximumSpindleSpeed: 2400, // specifies the maximum spindle speed + useParametricFeed: false, // specifies that feed should be output using Q values + showNotes: false, // specifies that operation notes should be output. + useCycles: true, // specifies that drilling cycles should be used. + autoEject: false, // specifies if the part should be automatically ejected at end of program + g53HomePositionX: 0, // home position for X-axis + g53HomePositionY: 0, // home position for Y-axis + g53HomePositionZ: 0, // home position for Z-axis + g53HomePositionSubZ: 0, // home Position for Z when the operation uses the Secondary Spindle + g53WorkPositionSub: 0, // home position for secondary spindle + useTailStock: false, // specifies to use the tailstock or not + useBarFeeder: false, // specifies to use the bar feeder + gotChipConveyor: false, // specifies to use a chip conveyor Y/N + useG112: false, // specifies if the machine has XY polar interpolation (G112) capabilities + useG61: false, // exact stop mode + // useM97: false, + setting102: 1.0, // diameter used by control to calculate feed rates (INCH value) + rapidRewinds: false, // rewinds the C-axis using G0 + useSSV: false, // outputs M38/39 to enable SSV in turning operations + optimizeCAxisSelect: false, // optimize output of enable/disable C-axis codes + gotSecondarySpindle: false, // specifies if the machine has a secondary spindle + looping: false, //output program for M97 looping + numberOfRepeats: 1, //how many times to loop program + useSimpleThread: true, // outputs a G92 threading cycle, false outputs a G76 (standard) threading cycle + airCleanChuck: true, // air clean chucks on transfer and eject + safeStartAllOperations: false // write optional blocks at the beginning of all operations that include all commands to start program +}; + +// user-defined property definitions +propertyDefinitions = { + writeMachine: {title:"Write machine", description:"Output the machine settings in the header of the code.", group:0, type:"boolean"}, + writeTools: {title:"Write tool list", description:"Output a tool list in the header of the code.", group:0, type:"boolean"}, + writeVersion: {title:"Write version", description:"Write the version number in the header of the code.", group:0, type:"boolean"}, + showSequenceNumbers: {title:"Use sequence numbers", description:"Use sequence numbers for each block of outputted code.", group:1, type:"boolean"}, + sequenceNumberStart: {title:"Start sequence number", description:"The number at which to start the sequence numbers.", group:1, type:"integer"}, + sequenceNumberIncrement: {title:"Sequence number increment", description:"The amount by which the sequence number is incremented by in each block.", group:1, type:"integer"}, + optionalStop: {title:"Optional stop", description:"Outputs optional stop code during when necessary in the code.", type:"boolean"}, + separateWordsWithSpace: {title:"Separate words with space", description:"Adds spaces between words if 'yes' is selected.", type:"boolean"}, + useRadius: {title:"Radius arcs", description:"If yes is selected, arcs are outputted using radius values rather than IJK.", type:"boolean"}, + maximumSpindleSpeed: {title:"Max spindle speed", description:"Defines the maximum spindle speed allowed by your machines.", type:"integer", range:[0, 999999999]}, + useParametricFeed: {title:"Parametric feed", description:"Specifies the feed value that should be output using a Q value.", type:"boolean"}, + showNotes: {title:"Show notes", description:"Writes operation notes as comments in the outputted code.", type:"boolean"}, + useCycles: {title:"Use cycles", description:"Specifies if canned drilling cycles should be used.", type:"boolean"}, + autoEject: {title:"Auto eject", description:"Specifies whether the part should automatically eject at the end of a program.", type:"boolean"}, + g53HomePositionX: {title:"G53 home position X", description:"G53 X-axis home position.", type:"number"}, + g53HomePositionY: {title:"G53 home position Y", description:"G53 Y-axis home position.", type:"number"}, + g53HomePositionZ: {title:"G53 home position Z", description:"G53 Z-axis home position.", type:"number"}, + g53HomePositionSubZ: {title:"G53 home position subspindle Z", description:"G53 Z-axis home position when Secondary Spindle is active.", type:"number"}, + g53WorkPositionSub: {title:"G53 subspindle working position", description:"G53 working position for Secondary Spindle when active.", type:"number"}, + useTailStock: {title:"Use tail stock", description:"Enable to use the tail stock.", type:"boolean"}, + useBarFeeder: {title:"Use bar feeder", description:"Enable to use the bar feeder.", type:"boolean"}, + gotChipConveyor: {title:"Got chip conveyor", description:"Specifies whether to use a chip conveyor.", type:"boolean", presentation:"yesno"}, + useG112: {title:"Use polar interpolation", description:"Enables polar interpolation output.", type:"boolean"}, + useG61: {title:"Use exact stop mode", description:"Enables exact stop mode.", type:"boolean"}, + setting102: {title: "Feed rate calculation diameter", description: "Defines the part diameter in inches that the control uses to calculate feed rates.", type: "spatial", range: [0.1, 9999.0]}, + rapidRewinds: {title: "Use G0 for rewinds", description: "Uses G0 moves for rewinding of the C-axis.", type: "boolean"}, + useSSV: {title: "Use SSV", description:"Outputs M38/M39 to enable SSV for turning operations.", type:"boolean"}, + optimizeCAxisSelect: {title:"Optimize C-axis selection", description:"Optimizes the output of enable/disable C-axis codes.", type:"boolean"}, + gotSecondarySpindle: {title:"Got secondary spindle", description:"Specifies if the machine has a secondary spindle.", type:"boolean"}, + looping: {title:"Use M97 looping", description:"Output program for M97 looping.", type:"boolean", presentation:"yesno", group:5}, + numberOfRepeats: {title:"Number of repeats", description:"How many times to loop the program.", type:"integer", range:[0, 99999999], group:5}, + useSimpleThread: {title:"Use simple threading cycle", description:"Enable to output G92 simple threading cycle, disable to output G76 standard threading cycle.", type:"boolean"}, + airCleanChuck: {title:"Air clean chucks", description:"Enable to use the air blast to clean out the chuck on part transfers and part ejection.", type:"boolean"}, + safeStartAllOperations: {title:"Safe start all operations", description:"Write optional blocks at the beginning of all operations that include all commands to start program.", type:"boolean"} +}; + +var permittedCommentChars = " ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789.,=_-"; + +var gFormat = createFormat({prefix:"G", decimals:0}); +var mFormat = createFormat({prefix:"M", decimals:0}); +var pFormat = createFormat({prefix:"P", decimals:0}); + +var spatialFormat = createFormat({decimals:(unit == MM ? 3 : 4), forceDecimal:true}); +var xFormat = createFormat({decimals:(unit == MM ? 3 : 4), forceDecimal:true, scale:2}); // diameter mode & IS SCALING POLAR COORDINATES +var yFormat = createFormat({decimals:(unit == MM ? 3 : 4), forceDecimal:true}); +var zFormat = createFormat({decimals:(unit == MM ? 3 : 4), forceDecimal:true}); +var rFormat = createFormat({decimals:(unit == MM ? 3 : 4), forceDecimal:true}); // radius +var abcFormat = createFormat({decimals:3, forceDecimal:true, scale:DEG}); +var cFormat = createFormat({decimals:3, forceDecimal:true, scale:DEG}); +var feedFormat = createFormat({decimals:(unit == MM ? 3 : 4), 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 milliFormat = createFormat({decimals:0}); // milliseconds // range 1-9999 +var taperFormat = createFormat({decimals:1, scale:DEG}); + +var xOutput = createVariable({prefix:"X"}, xFormat); +var yOutput = createVariable({prefix:"Y"}, yFormat); +var zOutput = createVariable({prefix:"Z"}, zFormat); +var aOutput = createVariable({prefix:"A"}, abcFormat); +var bOutput = createVariable({prefix:"B"}, abcFormat); +var cOutput = createVariable({prefix:"C"}, cFormat); +var feedOutput = createVariable({prefix:"F"}, feedFormat); +var pitchOutput = createVariable({prefix:"F", force:true}, pitchFormat); +var sOutput = createVariable({prefix:"S", force:true}, rpmFormat); +var pOutput = createVariable({prefix:"P", force:true}, rpmFormat); + +// circular output +var iOutput = createReferenceVariable({prefix:"I", force:true}, spatialFormat); +var jOutput = createReferenceVariable({prefix:"J", force:true}, spatialFormat); +var kOutput = createReferenceVariable({prefix:"K", force:true}, spatialFormat); + +// cycle thread output +var g76IOutput = createVariable({prefix:"I", force:true}, zFormat); // no scaling +var g76KOutput = createVariable({prefix:"K", force:true}, zFormat); // no scaling +var g76DOutput = createVariable({prefix:"D", force:true}, zFormat); // no scaling +var g76AOutput = createVariable({prefix:"A", force:true}, zFormat); // no scaling + +var gMotionModal = createModal({}, gFormat); // modal group 1 // G0-G3, ... +var gPlaneModal = createModal({onchange:function () {gMotionModal.reset();}}, gFormat); // modal group 2 // G17-19 +var gFeedModeModal = createModal({}, gFormat); // modal group 5 // G98-99 +var gSpindleModeModal = createModal({}, gFormat); // modal group 5 // G96-97 +var gSynchronizedSpindleModal = createModal({}, gFormat); // G198/G199 +var gSpindleModal = createModal({}, gFormat); // G14/G15 SPINDLE MODE +var gUnitModal = createModal({}, gFormat); // modal group 6 // G20-21 +var gCycleModal = createModal({}, gFormat); // modal group 9 // G81, ... +var gPolarModal = createModal({}, gFormat); // G112, G113 +var ssvModal = createModal({}, mFormat); // M38, M39 +var cAxisEngageModal = createModal({}, mFormat); +var cAxisBrakeModal = createModal({}, mFormat); +var gExactStopModal = createModal({}, gFormat); // modal group for exact stop codes +var tailStockModal = createModal({}, mFormat); + +// fixed settings +var firstFeedParameter = 100; +var usePolarCircular = false; + +var WARNING_WORK_OFFSET = 0; +var WARNING_REPEAT_TAPPING = 1; + +// collected state +var sequenceNumber; +var currentWorkOffset; +var optionalSection = false; +var forceSpindleSpeed = false; +var activeMovements; // do not use by default +var currentFeedId; +var forcePolarMode; +var bestABCIndex = undefined; +var partCutoff = false; +var ejectRoutine = false; + +// used to convert blocks to optional for safeStartAllOperations, might get used outside of onSection +var skipBlock = false; +var operationNeedsSafeStart = false; + +var machineState = { + liveToolIsActive: undefined, + cAxisIsEngaged: undefined, + machiningDirection: undefined, + mainSpindleIsActive: undefined, + subSpindleIsActive: undefined, + mainSpindleBrakeIsActive: undefined, + subSpindleBrakeIsActive: undefined, + tailstockIsActive: undefined, + usePolarMode: undefined, + useXZCMode: undefined, + axialCenterDrilling: undefined, + tapping: undefined, + currentBAxisOrientationTurning: new Vector(0, 0, 0), + feedPerRevolution: undefined, + stockTransferIsActive: false +}; + +/** G/M codes setup */ +function getCode(code) { + switch (code) { + case "PART_CATCHER_ON": + return mFormat.format(36); + case "PART_CATCHER_OFF": + return mFormat.format(37); + case "TAILSTOCK_ON": + machineState.tailstockIsActive = true; + return mFormat.format(21); + case "TAILSTOCK_OFF": + machineState.tailstockIsActive = false; + return mFormat.format(22); + case "ENGAGE_C_AXIS": + machineState.cAxisIsEngaged = true; + return cAxisEngageModal.format(154); + case "DISENGAGE_C_AXIS": + machineState.cAxisIsEngaged = false; + return cAxisEngageModal.format(155); + case "POLAR_INTERPOLATION_ON": + return gPolarModal.format(112); + case "POLAR_INTERPOLATION_OFF": + return gPolarModal.format(113); + case "STOP_SPINDLE": + if (machineState.liveToolIsActive) { + machineState.liveToolIsActive = false; + return mFormat.format(135); + } else if (machineState.mainSpindleIsActive) { + machineState.mainSpindleIsActive = false; + return mFormat.format(5); + } else if (machineState.subSpindleIsActive) { + machineState.subSpindleIsActive = false; + return mFormat.format(145); + } else { + return undefined; + } + case "START_LIVE_TOOL_CW": + machineState.liveToolIsActive = true; + return mFormat.format(133); + case "START_LIVE_TOOL_CCW": + machineState.liveToolIsActive = true; + return mFormat.format(134); + 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(143); + case "START_SUB_SPINDLE_CCW": + machineState.subSpindleIsActive = true; + return mFormat.format(144); + case "MAIN_SPINDLE_BRAKE_ON": + machineState.mainSpindleBrakeIsActive = true; + return cAxisBrakeModal.format(14); + case "MAIN_SPINDLE_BRAKE_OFF": + machineState.mainSpindleBrakeIsActive = false; + return cAxisBrakeModal.format(15); + case "SUB_SPINDLE_BRAKE_ON": + machineState.subSpindleBrakeIsActive = true; + return cAxisBrakeModal.format(114); + case "SUB_SPINDLE_BRAKE_OFF": + machineState.subSpindleBrakeIsActive = false; + return cAxisBrakeModal.format(115); + case "FEED_MODE_UNIT_REV": + machineState.feedPerRevolution = true; + return gFeedModeModal.format(99); + case "FEED_MODE_UNIT_MIN": + machineState.feedPerRevolution = false; + return gFeedModeModal.format(98); + 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(12); + case "MAINSPINDLE_AIR_BLAST_OFF": + return mFormat.format(13); + case "SUBSPINDLE_AIR_BLAST_ON": + return mFormat.format(112); + case "SUBSPINDLE_AIR_BLAST_OFF": + return mFormat.format(113); + case "CLAMP_PRIMARY_CHUCK": + return mFormat.format(10); + case "UNCLAMP_PRIMARY_CHUCK": + return mFormat.format(11); + case "CLAMP_SECONDARY_CHUCK": + return mFormat.format(110); + case "UNCLAMP_SECONDARY_CHUCK": + return mFormat.format(111); + case "SPINDLE_SYNCHRONIZATION_ON": + machineState.spindleSynchronizationIsActive = true; + return gSynchronizedSpindleModal.format(199); + case "SPINDLE_SYNCHRONIZATION_OFF": + machineState.spindleSynchronizationIsActive = false; + return gSynchronizedSpindleModal.format(198); + case "START_CHIP_TRANSPORT": + return mFormat.format(31); + case "STOP_CHIP_TRANSPORT": + return mFormat.format(33); + case "OPEN_DOOR": + return mFormat.format(85); + case "CLOSE_DOOR": + return mFormat.format(86); + case "COOLANT_FLOOD_ON": + return mFormat.format(8); + case "COOLANT_FLOOD_OFF": + return mFormat.format(9); + case "COOLANT_AIR_ON": + return mFormat.format(83); + case "COOLANT_AIR_OFF": + return mFormat.format(84); + case "COOLANT_THROUGH_TOOL_ON": + return mFormat.format(88); + case "COOLANT_THROUGH_TOOL_OFF": + return mFormat.format(89); + case "COOLANT_OFF": + return mFormat.format(9); + 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; +} + +function onSpindleSpeed(spindleSpeed) { + if ((sOutput.getCurrent() != Number.POSITIVE_INFINITY) && rpmFormat.areDifferent(spindleSpeed, sOutput.getCurrent())) { // avoid redundant output of spindle speed + startSpindle(false, getFramePosition(currentSection.getInitialPosition()), spindleSpeed); + } + if ((pOutput.getCurrent() != Number.POSITIVE_INFINITY) && rpmFormat.areDifferent(spindleSpeed, pOutput.getCurrent())) { // avoid redundant output of spindle speed + startSpindle(false, getFramePosition(currentSection.getInitialPosition()), spindleSpeed); + } +} + +function startSpindle(forceRPMMode, initialPosition, rpm) { + var _skipBlock = skipBlock; + var _spindleSpeed = spindleSpeed; + if (rpm !== undefined) { + _spindleSpeed = rpm; + } + + var useConstantSurfaceSpeed = currentSection.getTool().getSpindleMode() == SPINDLE_CONSTANT_SURFACE_SPEED; + var maximumSpindleSpeed = (tool.maximumSpindleSpeed > 0) ? Math.min(tool.maximumSpindleSpeed, properties.maximumSpindleSpeed) : properties.maximumSpindleSpeed; + if (useConstantSurfaceSpeed && !forceRPMMode) { + skipBlock = _skipBlock; + writeBlock(gFormat.format(50), sOutput.format(maximumSpindleSpeed)); + } + + gSpindleModeModal.reset(); + skipBlock = _skipBlock; + if (useConstantSurfaceSpeed && !forceRPMMode) { + writeBlock(getCode("CONSTANT_SURFACE_SPEED_ON")); + } else { + writeBlock(getCode("CONSTANT_SURFACE_SPEED_OFF")); + } + + _spindleSpeed = useConstantSurfaceSpeed ? tool.surfaceSpeed * ((unit == MM) ? 1 / 1000.0 : 1 / 12.0) : _spindleSpeed; + if (useConstantSurfaceSpeed && 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); + } + } + switch (currentSection.spindle) { + case SPINDLE_PRIMARY: // main spindle + if (machineState.isTurningOperation || machineState.axialCenterDrilling) { // turning main spindle + skipBlock = _skipBlock; + writeBlock( + sOutput.format(_spindleSpeed), + conditional(!machineState.tapping, tool.clockwise ? getCode("START_MAIN_SPINDLE_CW") : getCode("START_MAIN_SPINDLE_CCW")) + ); + } else { // milling main spindle + skipBlock = _skipBlock; + writeBlock( + (machineState.tapping ? sOutput.format(spindleSpeed) : pOutput.format(_spindleSpeed)), + conditional(!machineState.tapping, tool.clockwise ? getCode("START_LIVE_TOOL_CW") : getCode("START_LIVE_TOOL_CCW")) + ); + } + break; + case SPINDLE_SECONDARY: // sub spindle + if (!properties.gotSecondarySpindle) { + error(localize("Secondary spindle is not available.")); + return; + } + if (machineState.isTurningOperation || machineState.axialCenterDrilling) { // turning sub spindle + // use could also swap spindles using G14/G15 + gSpindleModeModal.reset(); + skipBlock = _skipBlock; + writeBlock( + sOutput.format(_spindleSpeed), + conditional(!machineState.tapping, tool.clockwise ? getCode("START_SUB_SPINDLE_CW") : getCode("START_SUB_SPINDLE_CCW")) + ); + } else { // milling sub spindle + skipBlock = _skipBlock; + writeBlock(pOutput.format(_spindleSpeed), tool.clockwise ? getCode("START_LIVE_TOOL_CW") : getCode("START_LIVE_TOOL_CCW")); + } + break; + } + + if (properties.useSSV) { + if (machineState.isTurningOperation && hasParameter("operation-strategy") && getParameter("operation-strategy") != "turningThread") { + skipBlock = _skipBlock; + writeBlock(ssvModal.format(38)); + } + } +} + +/** Write retract in XY/Z. */ +function writeRetract(section, retractZ) { + var _skipBlock = skipBlock; + if (!isFirstSection()) { + if (gotYAxis) { + skipBlock = _skipBlock; + writeBlock(gFormat.format(53), gMotionModal.format(0), "Y" + yFormat.format(properties.g53HomePositionY)); // retract + yOutput.reset(); + } + skipBlock = _skipBlock; + writeBlock(gFormat.format(53), gMotionModal.format(0), "X" + xFormat.format(properties.g53HomePositionX)); // retract + xOutput.reset(); + if (retractZ) { + skipBlock = _skipBlock; + writeBlock(gFormat.format(53), gMotionModal.format(0), "Z" + zFormat.format((section.spindle == SPINDLE_SECONDARY) ? properties.g53HomePositionSubZ : properties.g53HomePositionZ)); // retract with regard to spindle + zOutput.reset(); + } + } +} + +/** Write WCS. */ +function writeWCS(section) { + var workOffset = section.workOffset; + if (workOffset == 0) { + warningOnce(localize("Work offset has not been specified. Using G54 as WCS."), WARNING_WORK_OFFSET); + workOffset = 1; + } + if (workOffset > 0) { + if (workOffset > 6) { + var code = workOffset - 6; + if (code > 99) { + error(localize("Work offset out of range.")); + return; + } + if (workOffset != currentWorkOffset) { + forceWorkPlane(); + writeBlock(gFormat.format(154), "P" + code); + currentWorkOffset = workOffset; + } + } else { + if (workOffset != currentWorkOffset) { + forceWorkPlane(); + writeBlock(gFormat.format(53 + workOffset)); // G54->G59 + currentWorkOffset = workOffset; + } + } + } +} + +/** Returns the modulus. */ +function getModulus(x, y) { + return Math.sqrt(x * x + y * y); +} + +/** + Returns the C rotation for the given X and Y coordinates. +*/ +function getC(x, y) { + var direction; + if (Vector.dot(machineConfiguration.getAxisU().getAxis(), new Vector(0, 0, 1)) != 0) { + direction = (machineConfiguration.getAxisU().getAxis().getCoordinate(2) >= 0) ? 1 : -1; // C-axis is the U-axis + } else { + direction = (machineConfiguration.getAxisV().getAxis().getCoordinate(2) >= 0) ? 1 : -1; // C-axis is the V-axis + } + + return Math.atan2(y, x) * direction; +} + +/** + Returns the C rotation for the given X and Y coordinates in the desired rotary direction. +*/ +function getCClosest(x, y, _c, clockwise) { + if (_c == Number.POSITIVE_INFINITY) { + _c = 0; // undefined + } + if (!xFormat.isSignificant(x) && !yFormat.isSignificant(y)) { // keep C if XY is on center + return _c; + } + var c = getC(x, y); + if (clockwise != undefined) { + if (clockwise) { + while (c < _c) { + c += Math.PI * 2; + } + } else { + while (c > _c) { + c -= Math.PI * 2; + } + } + } else { + min = _c - Math.PI; + max = _c + Math.PI; + while (c < min) { + c += Math.PI * 2; + } + while (c > max) { + c -= Math.PI * 2; + } + } + return c; +} + +function getCWithinRange(x, y, _c, clockwise) { + var c = getCClosest(x, y, _c, clockwise); + + var cyclicLimit; + var cyclic; + if (Vector.dot(machineConfiguration.getAxisU().getAxis(), new Vector(0, 0, 1)) != 0) { + // C-axis is the U-axis + cyclicLimit = machineConfiguration.getAxisU().getRange(); + cyclic = machineConfiguration.getAxisU().isCyclic(); + } else if (Vector.dot(machineConfiguration.getAxisV().getAxis(), new Vector(0, 0, 1)) != 0) { + // C-axis is the V-axis + cyclicLimit = machineConfiguration.getAxisV().getRange(); + cyclic = machineConfiguration.getAxisV().isCyclic(); + } else { + error(localize("Unsupported rotary axis direction.")); + return 0; + } + + // see if rewind is required + forceRewind = false; + if ((cFormat.getResultingValue(c) < cFormat.getResultingValue(cyclicLimit[0])) || (cFormat.getResultingValue(c) > cFormat.getResultingValue(cyclicLimit[1]))) { + if (!cyclic) { + forceRewind = true; + } + c = getCClosest(x, y, 0); // find closest C to 0 + if ((cFormat.getResultingValue(c) < cFormat.getResultingValue(cyclicLimit[0])) || (cFormat.getResultingValue(c) > cFormat.getResultingValue(cyclicLimit[1]))) { + var midRange = cyclicLimit[0] + (cyclicLimit[1] - cyclicLimit[0]) / 2; + c = getCClosest(x, y, midRange); // find closest C to midRange + } + if ((cFormat.getResultingValue(c) < cFormat.getResultingValue(cyclicLimit[0])) || (cFormat.getResultingValue(c) > cFormat.getResultingValue(cyclicLimit[1]))) { + error(localize("Unable to find C-axis position within the defined range.")); + return 0; + } + } + return c; +} + +/** + Returns the desired tolerance for the given section. +*/ +function getTolerance() { + var t = tolerance; + if (hasParameter("operation:tolerance")) { + if (t > 0) { + t = Math.min(t, getParameter("operation:tolerance")); + } else { + t = getParameter("operation:tolerance"); + } + } + return t; +} + +/** + Writes the specified block. +*/ +function writeBlock() { + var text = formatWords(arguments); + if (!text) { + skipBlock = false; + return; + } + if (properties.showSequenceNumbers) { + if (sequenceNumber > 99999) { + sequenceNumber = properties.sequenceNumberStart; + } + if (optionalSection || skipBlock) { + if (text) { + writeWords("/", "N" + sequenceNumber, text); + } + } else { + writeWords2("N" + sequenceNumber, arguments); + } + sequenceNumber += properties.sequenceNumberIncrement; + } else { + if (optionalSection || skipBlock) { + writeWords2("/", arguments); + } else { + writeWords(arguments); + } + } + skipBlock = false; +} + +/** + Writes the specified optional block. +*/ +function writeOptionalBlock() { + if (properties.showSequenceNumbers) { + var words = formatWords(arguments); + if (words) { + writeWords("/", "N" + sequenceNumber, words); + sequenceNumber += properties.sequenceNumberIncrement; + } + } else { + writeWords2("/", arguments); + } +} + +function formatComment(text) { + return "(" + String(text).replace(/[()]/g, "") + ")"; +} + +/** + Output a comment. +*/ +function writeComment(text) { + writeln(formatComment(text)); +} + +function getB(abc, section) { + if (section.spindle == SPINDLE_PRIMARY) { + return abc.y; + } else { + return Math.PI - abc.y; + } +} + +var machineConfigurationMainSpindle; +var machineConfigurationSubSpindle; + +function onOpen() { + if (properties.useRadius) { + maximumCircularSweep = toRad(90); // avoid potential center calculation errors for CNC + } + + if (properties.useG61) { + gExactStopModal.format(64); + } + + if (true) { + var bAxisMain = createAxis({coordinate:1, table:false, axis:[0, -1, 0], range:[-0.001, 90.001], preference:0}); + var cAxisMain = createAxis({coordinate:2, table:true, axis:[0, 0, 1], cyclic:false, range:[-8280, 8280], preference:0}); + + var bAxisSub = createAxis({coordinate:1, table:false, axis:[0, -1, 0], range:[-0.001, 180.001], preference:0}); + var cAxisSub = createAxis({coordinate:2, table:true, axis:[0, 0, 1], cyclic:false, range:[-8280, 8280], preference:0}); + + machineConfigurationMainSpindle = gotBAxis ? new MachineConfiguration(bAxisMain, cAxisMain) : new MachineConfiguration(cAxisMain); + machineConfigurationSubSpindle = gotBAxis ? new MachineConfiguration(bAxisSub, cAxisSub) : new MachineConfiguration(cAxisSub); + } + + machineConfiguration = new MachineConfiguration(); // creates an empty configuration to be able to set eg vendor information + + machineConfiguration.setVendor("HAAS"); + machineConfiguration.setModel(description); + + if (!gotYAxis) { + yOutput.disable(); + } + aOutput.disable(); + if (!gotBAxis) { + bOutput.disable(); + } + + if (highFeedrate <= 0) { + error(localize("You must set 'highFeedrate' because axes are not synchronized for rapid traversal.")); + return; + } + + if (!properties.separateWordsWithSpace) { + setWordSeparator(""); + } + + sequenceNumber = properties.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 <= 99999))) { + error(localize("Program number is out of range.")); + return; + } + var oFormat = createFormat({width:5, 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; + } + + if (properties.writeVersion) { + if ((typeof getHeaderVersion == "function") && getHeaderVersion()) { + writeComment(localize("post version") + ": " + getHeaderVersion()); + } + if ((typeof getHeaderDate == "function") && getHeaderDate()) { + writeComment(localize("post modified") + ": " + getHeaderDate()); + } + } + + // dump machine configuration + var vendor = machineConfiguration.getVendor(); + var model = machineConfiguration.getModel(); + var description = machineConfiguration.getDescription(); + + if (properties.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); + } + } + + // dump tool information + if (properties.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); + } + } + } + + 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 (spatialFormat.areDifferent(tooli.diameter, toolj.diameter) || + spatialFormat.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; + } + } + } + + // support program looping for bar work + if (properties.looping) { + if (properties.numberOfRepeats < 1) { + error(localize("numberOfRepeats must be greater than 0.")); + return; + } + if (sequenceNumber == 1) { + sequenceNumber++; + } + writeln(""); + writeln(""); + writeComment(localize("Local Looping")); + writeln(""); + writeBlock(mFormat.format(97), "P1", "L" + properties.numberOfRepeats); + writeBlock(mFormat.format(30)); + writeln(""); + writeln(""); + writeln("N1 (START MAIN PROGRAM)"); + } + + // absolute coordinates and feed per min + writeBlock(getCode("FEED_MODE_UNIT_MIN"), gPlaneModal.format(18)); + + switch (unit) { + case IN: + writeBlock(gUnitModal.format(20)); + break; + case MM: + writeBlock(gUnitModal.format(21)); + break; + } + + // writeBlock("#" + (firstFeedParameter - 1) + "=" + ((currentSection.spindle == SPINDLE_SECONDARY) ? properties.g53HomePositionSubZ : properties.g53HomePositionZ), formatComment("g53HomePositionZ")); + + var usesPrimarySpindle = false; + var usesSecondarySpindle = false; + for (var i = 0; i < getNumberOfSections(); ++i) { + var section = getSection(i); + if (section.getType() != TYPE_TURNING) { + continue; + } + switch (section.spindle) { + case SPINDLE_PRIMARY: + usesPrimarySpindle = true; + break; + case SPINDLE_SECONDARY: + usesSecondarySpindle = true; + break; + } + } + + writeBlock(gFormat.format(50), sOutput.format(properties.maximumSpindleSpeed)); + sOutput.reset(); + + if (properties.gotChipConveyor) { + onCommand(COMMAND_START_CHIP_TRANSPORT); + } + + if (gotYAxis) { + writeBlock(gFormat.format(53), gMotionModal.format(0), "Y" + yFormat.format(properties.g53HomePositionY)); // retract + } + writeBlock(gFormat.format(53), gMotionModal.format(0), "X" + xFormat.format(properties.g53HomePositionX)); // retract + if (properties.gotSecondarySpindle) { + writeBlock(gFormat.format(53), gMotionModal.format(0), "B" + abcFormat.format(0)); // retract Sub Spindle if applicable + } + writeBlock(gFormat.format(53), gMotionModal.format(0), "Z" + zFormat.format(properties.g53HomePositionZ)); // retract + + // automatically eject part at end of program + if (properties.autoEject) { + ejectRoutine = true; + } +/* + if (properties.useM97) { + for (var i = 0; i < getNumberOfSections(); ++i) { + var section = getSection(i); + writeBlock(mFormat.format(97), pFormat.format(section.getId() + properties.sequenceNumberStart), conditional(section.hasParameter("operation-comment"), "(" + section.getParameter("operation-comment") + ")")); + } + writeBlock(mFormat.format(30)); + if (properties.showSequenceNumbers && properties.useM97) { + error(localize("Properties 'showSequenceNumbers' and 'useM97' cannot be active together at the same time.")); + return; + } + } +*/ +} + +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(); +} + +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 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; + 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)) { + var feedContext = new FeedContext(id, localize("High Feed"), this.highFeedrate); + activeFeeds.push(feedContext); + activeMovements[MOVEMENT_HIGH_FEED] = 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; + +function forceWorkPlane() { + currentWorkPlaneABC = undefined; +} + +function setWorkPlane(abc) { + if (!machineConfiguration.isMultiAxisConfiguration()) { + return; // ignore + } + + var _skipBlock = false; + if (!((currentWorkPlaneABC == undefined) || + abcFormat.areDifferent(abc.x, currentWorkPlaneABC.x) || + abcFormat.areDifferent(abc.y, currentWorkPlaneABC.y) || + abcFormat.areDifferent(abc.z, currentWorkPlaneABC.z))) { + if (operationNeedsSafeStart) { + _skipBlock = true; + } else { + return; // no change + } + } + + skipBlock = _skipBlock; + onCommand(COMMAND_UNLOCK_MULTI_AXIS); + gMotionModal.reset(); + + skipBlock = _skipBlock; + writeBlock( + gMotionModal.format(0), + conditional(machineConfiguration.isMachineCoordinate(0), "A" + abcFormat.format(abc.x)), + conditional(machineConfiguration.isMachineCoordinate(1), "B" + abcFormat.format(getB(abc, currentSection))), + conditional(machineConfiguration.isMachineCoordinate(2), "C" + abcFormat.format(abc.z)) + ); + + if (!currentSection.isMultiAxis() && !machineState.usePolarMode && !machineState.useXZCMode) { + skipBlock = _skipBlock; + onCommand(COMMAND_LOCK_MULTI_AXIS); + currentWorkPlaneABC = abc; + } else { + forceWorkPlane(); + } +} + +function getBestABC(section, which) { + var W = section.workPlane; + var abc = machineConfiguration.getABC(W); + if (which == undefined) { // turning, XZC, Polar modes + return abc; + } + if (Vector.dot(machineConfiguration.getAxisU().getAxis(), new Vector(0, 0, 1)) != 0) { + var axis = machineConfiguration.getAxisU(); // C-axis is the U-axis + } else { + var axis = machineConfiguration.getAxisV(); // C-axis is the V-axis + } + 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; + var box = section.getGlobalBoundingBox(); + switch (which) { + case 1: + x = box.lower.x + ((box.upper.x - box.lower.x) / 2); + y = box.lower.y + ((box.upper.y - box.lower.y) / 2); + break; + case 2: + x = box.lower.x; + y = box.lower.y; + break; + case 3: + x = box.upper.x; + y = box.lower.y; + break; + case 4: + x = box.upper.x; + y = box.upper.y; + break; + case 5: + x = box.lower.x; + y = box.upper.y; + break; + default: + var R = machineConfiguration.getRemainingOrientation(abc, W); + x = R.right.x; + y = R.right.y; + break; + } + abc.setCoordinate(ix, getCClosest(x, y, cOutput.getCurrent())); + } + } + // writeComment("Which = " + which + " Angle = " + abc.z) + return abc; +} + +var closestABC = false; // choose closest machine angles +var currentMachineABC; + +function getWorkPlaneMachineABC(section, workPlane) { + var W = workPlane; // map to global frame + + var abc; + if (machineState.isTurningOperation && gotBAxis) { + var both = machineConfiguration.getABCByDirectionBoth(workPlane.forward); + abc = both[0]; + if (both[0].z != 0) { + abc = both[1]; + } + } else { + abc = getBestABC(section, bestABCIndex); + if (closestABC) { + if (currentMachineABC) { + abc = machineConfiguration.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 (machineState.isTurningOperation && gotBAxis) { // remapABC can change the B-axis orientation + if (abc.z != 0) { + error(localize("Could not calculate a B-axis turning angle within the range of the machine.")); + return abc; + } + } + + 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)) + ); + } + + if (!machineState.isTurningOperation) { + var tcp = false; + if (tcp) { + setRotation(W); // TCP mode + } else { + var O = machineConfiguration.getOrientation(abc); + var R = machineConfiguration.getRemainingOrientation(abc, W); + setRotation(R); + } + } + + return abc; +} + +function getBAxisOrientationTurning(section) { + var toolAngle = hasParameter("operation:tool_angle") ? getParameter("operation:tool_angle") : 0; + var toolOrientation = section.toolOrientation; + if (toolAngle && (toolOrientation != 0)) { + error(localize("You cannot use tool angle and tool orientation together in operation " + "\"" + (getParameter("operation-comment")) + "\"")); + } + + var angle = toRad(toolAngle) + toolOrientation; + + var axis = new Vector(0, 1, 0); + var mappedAngle = (currentSection.spindle == SPINDLE_PRIMARY ? (Math.PI / 2 - angle) : (Math.PI / 2 - angle)); + var mappedWorkplane = new Matrix(axis, mappedAngle); + var abc = getWorkPlaneMachineABC(section, mappedWorkplane); + + return abc; +} + +function setSpindleOrientationTurning(section) { + var J; // cutter orientation + var R; // cutting quadrant + var leftHandTool = (hasParameter("operation:tool_hand") && (getParameter("operation:tool_hand") == "L" || getParameter("operation:tool_holderType") == 0)); + if (hasParameter("operation:machineInside")) { + if (getParameter("operation:machineInside") == 0) { + R = currentSection.spindle == SPINDLE_PRIMARY ? 3 : 4; + } else { + R = currentSection.spindle == SPINDLE_PRIMARY ? 2 : 1; + } + } else { + if ((hasParameter("operation-strategy") && (getParameter("operation-strategy") == "turningFace")) || + (hasParameter("operation-strategy") && (getParameter("operation-strategy") == "turningPart"))) { + R = currentSection.spindle == SPINDLE_PRIMARY ? 3 : 4; + } else { + error(subst(localize("Failed to identify spindle orientation for operation \"%1\"."), getOperationComment())); + return; + } + } + if (leftHandTool) { + J = currentSection.spindle == (SPINDLE_PRIMARY ? 2 : 1); + } else { + J = currentSection.spindle == (SPINDLE_PRIMARY ? 1 : 2); + } + writeComment("Post processor is not customized, add code for cutter orientation and cutting quadrant here if needed."); +} + +var bAxisOrientationTurning = new Vector(0, 0, 0); + +function onSection() { + // Detect machine configuration + machineConfiguration = (currentSection.spindle == SPINDLE_PRIMARY) ? machineConfigurationMainSpindle : machineConfigurationSubSpindle; + if (!gotBAxis) { + if (getMachiningDirection(currentSection) == MACHINING_DIRECTION_AXIAL && !currentSection.isMultiAxis()) { + machineConfiguration.setSpindleAxis(new Vector(0, 0, 1)); + } else { + machineConfiguration.setSpindleAxis(new Vector(1, 0, 0)); + } + } else { + machineConfiguration.setSpindleAxis(new Vector(0, 0, 1)); // set the spindle axis depending on B0 orientation + } + + setMachineConfiguration(machineConfiguration); + currentSection.optimizeMachineAnglesByMachine(machineConfiguration, 1); // map tip mode + + var previousTapping = machineState.tapping; + machineState.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 forceToolAndRetract = optionalSection && !currentSection.isOptional(); + optionalSection = currentSection.isOptional(); + bestABCIndex = undefined; + + machineState.isTurningOperation = (currentSection.getType() == TYPE_TURNING); + if (machineState.isTurningOperation && gotBAxis) { + bAxisOrientationTurning = getBAxisOrientationTurning(currentSection); + } + partCutoff = hasParameter("operation-strategy") && (getParameter("operation-strategy") == "turningPart"); + 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); + insertToolCall = (machineState.stockTransferIsActive && partCutoff) ? false : insertToolCall; // tool is loaded during stock transfer op + + var retracted = false; // specifies that the tool has been retracted to the safe plane + var newSpindle = isFirstSection() || + (getPreviousSection().spindle != currentSection.spindle); + var newWorkOffset = isFirstSection() || + (getPreviousSection().workOffset != currentSection.workOffset); // work offset changes + var newWorkPlane = isFirstSection() || + !isSameDirection(getPreviousSection().getGlobalFinalToolAxis(), currentSection.getGlobalInitialToolAxis()) || + (machineState.isTurningOperation && + abcFormat.areDifferent(bAxisOrientationTurning.x, machineState.currentBAxisOrientationTurning.x) || + abcFormat.areDifferent(bAxisOrientationTurning.y, machineState.currentBAxisOrientationTurning.y) || + abcFormat.areDifferent(bAxisOrientationTurning.z, machineState.currentBAxisOrientationTurning.z)); + + operationNeedsSafeStart = properties.safeStartAllOperations && !isFirstSection(); + + if (insertToolCall || newSpindle || newWorkOffset || newWorkPlane && + (!currentSection.isPatterned() && (!machineState.stockTransferIsActive && !partCutoff))) { + + // retract to safe plane + retracted = true; + if (!isFirstSection()) { + if (insertToolCall) { + onCommand(COMMAND_COOLANT_OFF); + } + writeRetract(currentSection, true); // retract in Z also + } + } + + var yAxisWasEnabled = !machineState.useXZCMode && !machineState.usePolarMode && machineState.liveToolIsActive; + updateMachiningMode(currentSection); // sets the needed machining mode to machineState (usePolarMode, useXZCMode, axialCenterDrilling) + + if (currentSection.getTool().isLiveTool) { + if (!isFirstSection() && + ((getPreviousSection().getTool().isLiveTool() != currentSection.getTool().isLiveTool()) || + (previousTapping && insertToolCall))) { + writeBlock(getCode("STOP_SPINDLE")); + } + } else { + writeBlock(getCode("STOP_SPINDLE")); + } + + /* + if (properties.useM97 && !isFirstSection()) { + writeBlock(mFormat.format(99)); + } +*/ + + if (properties.useSSV) { + // ensure SSV is turned off + writeBlock(ssvModal.format(39)); + } + + writeln(""); + + /* + if (properties.useM97) { + writeBlock("N" + spatialFormat.format(currentSection.getId() + properties.sequenceNumberStart)); + } +*/ + + // Consider part cutoff as stockTransfer operation + if (!(machineState.stockTransferIsActive && partCutoff)) { + machineState.stockTransferIsActive = false; + } + + if (hasParameter("operation-comment")) { + var comment = getParameter("operation-comment"); + if (comment) { + writeComment(comment); + } + } + + if (!insertToolCall && operationNeedsSafeStart) { + skipBlock = true; + writeRetract(currentSection, true); // retract in Z also + } + + if (properties.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 || operationNeedsSafeStart) { + if (insertToolCall) { + forceWorkPlane(); + } + if (!properties.optimizeCAxisSelect) { + cAxisEngageModal.reset(); + } + retracted = insertToolCall; + + if (!isFirstSection() && properties.optionalStop) { + skipBlock = !insertToolCall; + onCommand(COMMAND_OPTIONAL_STOP); + } + + /** Handle multiple turrets. */ + if (gotMultiTurret) { + var activeTurret = tool.turret; + if (activeTurret == 0) { + warning(localize("Turret has not been specified. Using Turret 1 as default.")); + activeTurret = 1; // upper turret as default + } + switch (activeTurret) { + case 1: + // add specific handling for turret 1 + break; + case 2: + // add specific handling for turret 2, normally X-axis is reversed for the lower turret + //xFormat = createFormat({decimals:(unit == MM ? 3 : 4), forceDecimal:true, scale:-1}); // inverted diameter mode + //xOutput = createVariable({prefix:"X"}, xFormat); + break; + default: + error(localize("Turret is not supported.")); + } + } + + if (tool.number > 99) { + warning(localize("Tool number exceeds maximum value.")); + } + + var compensationOffset = tool.isTurningTool() ? tool.compensationOffset : tool.lengthOffset; + if (compensationOffset > 99) { + error(localize("Compensation offset is out of range.")); + return; + } + + if (properties.gotSecondarySpindle) { + switch (currentSection.spindle) { + case SPINDLE_PRIMARY: // main spindle + skipBlock = !insertToolCall; + writeBlock(gSpindleModal.format(15)); + break; + case SPINDLE_SECONDARY: // sub spindle + skipBlock = !insertToolCall; + writeBlock(gSpindleModal.format(14)); + break; + } + } + + skipBlock = !insertToolCall; + writeBlock("T" + toolFormat.format(tool.number * 100 + compensationOffset)); + if (tool.comment) { + writeComment(tool.comment); + } + + var showToolZMin = false; + if (showToolZMin && (currentSection.getType() == TYPE_MILLING)) { + 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 (properties.preloadTool) { + var nextTool = getNextTool(tool.number); + if (nextTool) { + var compensationOffset = nextTool.isTurningTool() ? nextTool.compensationOffset : nextTool.lengthOffset; + if (compensationOffset > 99) { + error(localize("Compensation offset is out of range.")); + return; + } + writeBlock("T" + toolFormat.format(nextTool.number * 100 + compensationOffset)); + } else { + // preload first tool + var section = getSection(0); + var firstTool = section.getTool().number; + if (tool.number != firstTool.number) { + var compensationOffset = firstTool.isTurningTool() ? firstTool.compensationOffset : firstTool.lengthOffset; + if (compensationOffset > 99) { + error(localize("Compensation offset is out of range.")); + return; + } + writeBlock("T" + toolFormat.format(firstTool.number * 100 + compensationOffset)); + } + } + } +*/ + } + + if (!machineState.stockTransferIsActive) { + if (machineState.isTurningOperation || machineState.axialCenterDrilling) { + skipBlock = !insertToolCall && (machineState.cAxisIsEngaged != undefined); + writeBlock(conditional(machineState.cAxisIsEngaged || machineState.cAxisIsEngaged == undefined), getCode("DISENGAGE_C_AXIS")); + } else { // milling + writeBlock(conditional(!machineState.cAxisIsEngaged || machineState.cAxisIsEngaged == undefined), getCode("ENGAGE_C_AXIS")); + } + } + + // command stop for manual tool change, useful for quick change live tools + if ((insertToolCall || operationNeedsSafeStart) && tool.manualToolChange) { + skipBlock = !insertToolCall; + onCommand(COMMAND_STOP); + writeBlock("(" + "MANUAL TOOL CHANGE TO T" + toolFormat.format(tool.number * 100 + compensationOffset) + ")"); + } + + if (newSpindle) { + // select spindle if required + } + + gFeedModeModal.reset(); + if ((currentSection.feedMode == FEED_PER_REVOLUTION) || machineState.tapping || machineState.axialCenterDrilling) { + writeBlock(getCode("FEED_MODE_UNIT_REV")); // unit/rev + } else { + writeBlock(getCode("FEED_MODE_UNIT_MIN")); // unit/min + } + + // Engage tailstock + if (properties.useTailStock) { + if (machineState.axialCenterDrilling || (currentSection.spindle == SPINDLE_SECONDARY) || + (machineState.liveToolIsActive && (getMachiningDirection(currentSection) == MACHINING_DIRECTION_AXIAL))) { + if (currentSection.tailstock) { + warning(localize("Tail stock is not supported for secondary spindle or Z-axis milling.")); + } + if (machineState.tailstockIsActive) { + writeBlock(getCode("TAILSTOCK_OFF")); + } + } else { + writeBlock(currentSection.tailstock ? getCode("TAILSTOCK_ON") : getCode("TAILSTOCK_OFF")); + } + } + + // see page 138 in 96-8700an for stock transfer / G199/G198 + var spindleChange = forceSpindleSpeed || newSpindle || isSpindleSpeedDifferent() || (!machineState.liveToolIsActive && !machineState.mainSpindleIsActive && !machineState.subSpindleIsActive); + if (!machineState.stockTransferIsActive) { + if (insertToolCall || operationNeedsSafeStart || + spindleChange || + isFirstSection()) { + if (machineState.isTurningOperation) { + if (spindleSpeed > 99999) { + warning(subst(localize("Spindle speed exceeds maximum value for operation \"%1\"."), getOperationComment())); + } + } else { + if (spindleSpeed > 6000) { + warning(subst(localize("Spindle speed exceeds maximum value for operation \"%1\"."), getOperationComment())); + } + } + skipBlock = !insertToolCall && !spindleChange; + startSpindle(true, getFramePosition(currentSection.getInitialPosition())); + } + } + + // wcs + if (insertToolCall) { // force work offset when changing tool + currentWorkOffset = undefined; + } + var workOffset = currentSection.workOffset; + writeWCS(currentSection); + + // set coolant after we have positioned at Z + setCoolant(tool.coolant); + + if (currentSection.partCatcher) { + engagePartCatcher(true); + } + + forceAny(); + gMotionModal.reset(); + + gPlaneModal.reset(); + writeBlock(gPlaneModal.format(getPlane())); + + var abc = new Vector(0, 0, 0); + if (machineConfiguration.isMultiAxisConfiguration()) { + if (machineState.isTurningOperation || machineState.axialCenterDrilling) { + if (gotBAxis) { + // TAG: handle B-axis support for turning operations here + writeBlock(gMotionModal.format(0), conditional(machineConfiguration.isMachineCoordinate(1), bOutput.format(getB(bAxisOrientationTurning, currentSection)))); + machineState.currentBAxisOrientationTurning = bAxisOrientationTurning; + //setSpindleOrientationTurning(); + } else { + setRotation(currentSection.workPlane); + } + } else { + if (currentSection.isMultiAxis()) { + forceWorkPlane(); + cancelTransformation(); + onCommand(COMMAND_UNLOCK_MULTI_AXIS); + abc = currentSection.getInitialToolAxisABC(); + } else { + if (machineState.useXZCMode) { + setRotation(currentSection.workPlane); // enables calculation of the C-axis by tool XY-position + abc = new Vector(0, 0, getCWithinRange(getFramePosition(currentSection.getInitialPosition()).x, getFramePosition(currentSection.getInitialPosition()).y, cOutput.getCurrent())); + } else { + abc = getWorkPlaneMachineABC(currentSection, 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 by the CNC machine.")); + return; + } + setRotation(remaining); + } + forceAny(); + if (abc !== undefined) { + if (!currentSection.isMultiAxis()) { + cOutput.format(abc.z); // make C current - we do not want to output here + previousABC.setZ(abc.z); + } + } + + if (machineState.cAxisIsEngaged) { // make sure C-axis in engaged + if (!machineState.usePolarMode && !machineState.useXZCMode && !currentSection.isMultiAxis()) { + onCommand(COMMAND_LOCK_MULTI_AXIS); + } else { + onCommand(COMMAND_UNLOCK_MULTI_AXIS); + } + } + + var initialPosition = getFramePosition(currentSection.getInitialPosition()); + /* + if (!retracted) { + // TAG: need to retract along X or Z + if (getCurrentPosition().z < initialPosition.z) { + writeBlock(gMotionModal.format(0), zOutput.format(initialPosition.z)); + } + } +*/ + if ((machineState.useXZCMode || machineState.usePolarMode) && yAxisWasEnabled) { + if (gotYAxis && yOutput.isEnabled()) { + writeBlock(gMotionModal.format(0), yOutput.format(0)); + } + } + if (machineState.usePolarMode) { + setPolarMode(true); // enable polar interpolation mode + } + gMotionModal.reset(); + + if (properties.useG61) { + writeBlock(gExactStopModal.format(61)); + } + + if (insertToolCall || retracted || machineState.useXZCMode || (tool.getSpindleMode() == SPINDLE_CONSTANT_SURFACE_SPEED)) { + gMotionModal.reset(); + if (machineState.useXZCMode) { + writeBlock(gMotionModal.format(0), zOutput.format(initialPosition.z)); + writeBlock( + gMotionModal.format(0), + xOutput.format(getModulus(initialPosition.x, initialPosition.y)), + conditional(gotYAxis, yOutput.format(0)), + cOutput.format(getCWithinRange(initialPosition.x, initialPosition.y, cOutput.getCurrent())) + ); + previousABC.setZ(cOutput.getCurrent()); + } else { + writeBlock(gMotionModal.format(0), zOutput.format(initialPosition.z)); + writeBlock(gMotionModal.format(0), xOutput.format(initialPosition.x), yOutput.format(initialPosition.y)); + } + } + + // enable SFM spindle speed + if (tool.getSpindleMode() == SPINDLE_CONSTANT_SURFACE_SPEED) { + startSpindle(false); + } + + if (properties.useParametricFeed && + hasParameter("operation-strategy") && + (getParameter("operation-strategy") != "drill") && // legacy + !(currentSection.hasAnyCycle && currentSection.hasAnyCycle()) && + !machineState.useXZCMode) { + if (!insertToolCall && + activeMovements && + (getCurrentSectionId() > 0) && + ((getPreviousSection().getPatternId() == currentSection.getPatternId()) && (currentSection.getPatternId() != 0))) { + // use the current feeds + } else { + initializeActiveFeeds(); + } + } else { + activeMovements = undefined; + } + + if (false) { // DEBUG + for (var key in machineState) { + writeComment(key + " : " + machineState[key]); + } + // writeComment((getMachineConfigurationAsText(machineConfiguration))); + } +} + +function getPlane() { + if (getMachiningDirection(currentSection) == MACHINING_DIRECTION_AXIAL) { // axial + if (machineState.useXZCMode || + (currentSection.hasParameter("operation-strategy") && (currentSection.getParameter("operation-strategy") == "drill")) || + machineState.isTurningOperation) { + return 18; + } else { + return 17; // G112 and XY milling only + } + } else if (getMachiningDirection(currentSection) == MACHINING_DIRECTION_RADIAL) { // radial + return 19; // YZ plane + } else { + error(subst(localize("Unsupported machining direction for operation " + "\"" + "%1" + "\"" + "."), getOperationComment())); + return undefined; + } +} + +/** Returns true if the toolpath fits within the machine XY limits for the given C orientation. */ +function doesToolpathFitInXYRange(abc) { + var c = 0; + if (abc) { + c = abc.z; + } + + var dx = new Vector(Math.cos(c), Math.sin(c), 0); + var dy = new Vector(Math.cos(c + Math.PI / 2), Math.sin(c + Math.PI / 2), 0); + + if (currentSection.getGlobalRange) { + var xRange = currentSection.getGlobalRange(dx); + var yRange = currentSection.getGlobalRange(dy); + + if (false) { // DEBUG + writeComment("toolpath X min: " + xFormat.format(xRange[0]) + ", " + "Limit " + xFormat.format(xAxisMinimum)); + writeComment("X-min within range: " + (xFormat.getResultingValue(xRange[0]) >= xFormat.getResultingValue(xAxisMinimum))); + writeComment("toolpath Y min: " + spatialFormat.getResultingValue(yRange[0]) + ", " + "Limit " + yAxisMinimum); + writeComment("Y-min within range: " + (spatialFormat.getResultingValue(yRange[0]) >= yAxisMinimum)); + writeComment("toolpath Y max: " + (spatialFormat.getResultingValue(yRange[1]) + ", " + "Limit " + yAxisMaximum)); + writeComment("Y-max within range: " + (spatialFormat.getResultingValue(yRange[1]) <= yAxisMaximum)); + } + + if (getMachiningDirection(currentSection) == MACHINING_DIRECTION_RADIAL) { // G19 plane + if ((spatialFormat.getResultingValue(yRange[0]) >= yAxisMinimum) && + (spatialFormat.getResultingValue(yRange[1]) <= yAxisMaximum)) { + return true; // toolpath does fit in XY range + } else { + return false; // toolpath does not fit in XY range + } + } else { // G17 plane + if ((xFormat.getResultingValue(xRange[0]) >= xFormat.getResultingValue(xAxisMinimum)) && + (spatialFormat.getResultingValue(yRange[0]) >= yAxisMinimum) && + (spatialFormat.getResultingValue(yRange[1]) <= yAxisMaximum)) { + return true; // toolpath does fit in XY range + } else { + return false; // toolpath does not fit in XY range + } + } + } else { + if (revision < 40000) { + warning(localize("Please update to the latest release to allow XY linear interpolation instead of polar interpolation.")); + } + return false; // for older versions without the getGlobalRange() function + } +} + +var MACHINING_DIRECTION_AXIAL = 0; +var MACHINING_DIRECTION_RADIAL = 1; +var MACHINING_DIRECTION_INDEXING = 2; + +function getMachiningDirection(section) { + var forward = section.isMultiAxis() ? section.getGlobalInitialToolAxis() : section.workPlane.forward; + if (isSameDirection(forward, new Vector(0, 0, 1))) { + machineState.machiningDirection = MACHINING_DIRECTION_AXIAL; + return MACHINING_DIRECTION_AXIAL; + } else if (Vector.dot(forward, new Vector(0, 0, 1)) < 1e-7) { + machineState.machiningDirection = MACHINING_DIRECTION_RADIAL; + return MACHINING_DIRECTION_RADIAL; + } else { + machineState.machiningDirection = MACHINING_DIRECTION_INDEXING; + return MACHINING_DIRECTION_INDEXING; + } +} + +function updateMachiningMode(section) { + machineState.axialCenterDrilling = false; // reset + machineState.usePolarMode = false; // reset + machineState.useXZCMode = false; // reset + + if ((section.getType() == TYPE_MILLING) && !section.isMultiAxis()) { + if (getMachiningDirection(section) == MACHINING_DIRECTION_AXIAL) { + if (section.hasParameter("operation-strategy") && (section.getParameter("operation-strategy") == "drill")) { + // drilling axial + if ((section.getNumberOfCyclePoints() == 1) && + !xFormat.isSignificant(getGlobalPosition(section.getInitialPosition()).x) && + !yFormat.isSignificant(getGlobalPosition(section.getInitialPosition()).y) && + (spatialFormat.format(section.getFinalPosition().x) == 0) && + !doesCannedCycleIncludeYAxisMotion()) { // catch drill issue for old versions + // single hole on XY center + if (section.getTool().isLiveTool && section.getTool().isLiveTool()) { + // use live tool + } else { + // use main spindle for axialCenterDrilling + machineState.axialCenterDrilling = true; + } + } else { + // several holes not on XY center, use live tool in XZCMode + machineState.useXZCMode = true; + } + } else { // milling + fitFlag = false; + bestABCIndex = undefined; + for (var i = 0; i < 6; ++i) { + fitFlag = doesToolpathFitInXYRange(getBestABC(section, i)); + if (fitFlag) { + bestABCIndex = i; + break; + } + } + if (fitFlag) { + if (forcePolarMode) { // polar mode is requested by user + machineState.usePolarMode = true; + bestABCIndex = undefined; + } else { + // toolpath matches XY ranges, keep false + } + } else { + // toolpath does not match XY ranges, enable interpolation mode + if (properties.useG112 || forcePolarMode) { + machineState.usePolarMode = true; + } else { + machineState.useXZCMode = true; + } + } + } + } else if (getMachiningDirection(section) == MACHINING_DIRECTION_RADIAL) { // G19 plane + if (!gotYAxis) { + if (!section.isMultiAxis() && !doesToolpathFitInXYRange(machineConfiguration.getABC(section.workPlane)) && doesCannedCycleIncludeYAxisMotion()) { + error(subst(localize("Y-axis motion is not possible without a Y-axis for operation \"%1\"."), getOperationComment())); + return; + } + } else { + if (!doesToolpathFitInXYRange(machineConfiguration.getABC(section.workPlane))) { + error(subst(localize("Toolpath exceeds the maximum ranges for operation \"%1\"."), getOperationComment())); + return; + } + } + // C-coordinates come from setWorkPlane or is within a multi axis operation, we cannot use the C-axis for non wrapped toolpathes (only multiaxis works, all others have to be into XY range) + } else { + // useXZCMode & usePolarMode is only supported for axial machining, keep false + } + } else { + // turning or multi axis, keep false + } + + if (machineState.axialCenterDrilling) { + cOutput.disable(); + } else { + cOutput.enable(); + } + + var checksum = 0; + checksum += machineState.usePolarMode ? 1 : 0; + checksum += machineState.useXZCMode ? 1 : 0; + checksum += machineState.axialCenterDrilling ? 1 : 0; + validate(checksum <= 1, localize("Internal post processor error.")); +} + +function doesCannedCycleIncludeYAxisMotion() { + // these cycles have Y axis motions which are not detected by getGlobalRange() + var hasYMotion = false; + if (hasParameter("operation:strategy") && (getParameter("operation:strategy") == "drill")) { + switch (getParameter("operation:cycleType")) { + case "thread-milling": + case "bore-milling": + case "circular-pocket-milling": + hasYMotion = true; // toolpath includes Y-axis motion + break; + case "back-boring": + case "fine-boring": + var shift = getParameter("operation:boringShift"); + if (shift != spatialFormat.format(0)) { + hasYMotion = true; // toolpath includes Y-axis motion + } + break; + default: + hasYMotion = false; // all other cycles don´t have Y-axis motion + } + } else { + hasYMotion = true; + } + return hasYMotion; +} + +function getOperationComment() { + var operationComment = hasParameter("operation-comment") && getParameter("operation-comment"); + return operationComment; +} + +function setPolarMode(activate) { + if (activate) { + writeBlock(gMotionModal.format(0), cOutput.format(0)); // set C-axis to 0 to avoid G112 issues + writeBlock(getCode("POLAR_INTERPOLATION_ON")); // command for polar interpolation + writeBlock(gPlaneModal.format(getPlane())); + validate(gPlaneModal.getCurrent() == 17, localize("Internal post processor error.")); // make sure that G17 is active + xFormat.setScale(1); // radius mode + xOutput = createVariable({prefix:"X"}, xFormat); + yOutput.enable(); // required for G112 + } else { + writeBlock(getCode("POLAR_INTERPOLATION_OFF")); + xFormat.setScale(2); // diameter mode + xOutput = createVariable({prefix:"X"}, xFormat); + if (!gotYAxis) { + yOutput.disable(); + } + } +} + +function onDwell(seconds) { + if (seconds > 99999.999) { + warning(localize("Dwelling time is out of range.")); + } + milliseconds = clamp(1, seconds * 1000, 99999999); + writeBlock(gFormat.format(4), "P" + milliFormat.format(milliseconds)); +} + +var pendingRadiusCompensation = -1; + +function onRadiusCompensation() { + pendingRadiusCompensation = radiusCompensation; +} + +var resetFeed = false; + +function getHighfeedrate(radius) { + if (currentSection.feedMode == FEED_PER_REVOLUTION) { + if (toDeg(radius) <= 0) { + radius = toPreciseUnit(0.1, MM); + } + var rpm = spindleSpeed; // rev/min + if (currentSection.getTool().getSpindleMode() == SPINDLE_CONSTANT_SURFACE_SPEED) { + var O = 2 * Math.PI * radius; // in/rev + rpm = tool.surfaceSpeed / O; // in/min div in/rev => rev/min + } + return highFeedrate / rpm; // in/min div rev/min => in/rev + } + return highFeedrate; +} + +function onRapid(_x, _y, _z) { + if (machineState.useXZCMode) { + var start = getCurrentPosition(); + var dxy = getModulus(_x - start.x, _y - start.y); + if (true || (dxy < getTolerance())) { + var x = xOutput.format(getModulus(_x, _y)); + var c = cOutput.format(getCWithinRange(_x, _y, cOutput.getCurrent())); + var z = zOutput.format(_z); + if (pendingRadiusCompensation >= 0) { + error(localize("Radius compensation mode cannot be changed at rapid traversal.")); + return; + } + if (forceRewind) { + rewindTable(start, _z, cOutput.getCurrent(), highFeedrate, false); + } + writeBlock(gMotionModal.format(0), x, c, z); + previousABC.setZ(cOutput.getCurrent()); + forceFeed(); + return; + } + + onExpandedLinear(_x, _y, _z, highFeedrate); + return; + } + + var x = xOutput.format(_x); + var y = yOutput.format(_y); + var z = zOutput.format(_z); + if (x || y || z) { + var useG1 = ((x ? 1 : 0) + (y ? 1 : 0) + (z ? 1 : 0)) > 1 && !isCannedCycle; + if (pendingRadiusCompensation >= 0) { + pendingRadiusCompensation = -1; + if (useG1) { + switch (radiusCompensation) { + case RADIUS_COMPENSATION_LEFT: + writeBlock(gMotionModal.format(1), gFormat.format(41), x, y, z, getFeed(getHighfeedrate(_x))); + break; + case RADIUS_COMPENSATION_RIGHT: + writeBlock(gMotionModal.format(1), gFormat.format(42), x, y, z, getFeed(getHighfeedrate(_x))); + break; + default: + writeBlock(gMotionModal.format(1), gFormat.format(40), x, y, z, getFeed(getHighfeedrate(_x))); + } + } else { + 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); + } + } + } + if (false) { + // axes are not synchronized + writeBlock(gMotionModal.format(1), x, y, z, getFeed(getHighfeedrate(_x))); + resetFeed = false; + } else { + writeBlock(gMotionModal.format(0), x, y, z); + forceFeed(); + } + } +} + +/** Calculate the distance of a point to a line segment. */ +function pointLineDistance(startPt, endPt, testPt) { + var delta = Vector.diff(endPt, startPt); + distance = Math.abs(delta.y * testPt.x - delta.x * testPt.y + endPt.x * startPt.y - endPt.y * startPt.x) / + Math.sqrt(delta.y * delta.y + delta.x * delta.x); // distance from line to point + if (distance < 1e-4) { // make sure point is in line segment + var moveLength = Vector.diff(endPt, startPt).length; + var startLength = Vector.diff(startPt, testPt).length; + var endLength = Vector.diff(endPt, testPt).length; + if ((startLength > moveLength) || (endLength > moveLength)) { + distance = Math.min(startLength, endLength); + } + } + return distance; +} + +/** Refine segment for XC mapping. */ +function refineSegmentXC(startX, startC, endX, endC, maximumDistance) { + var rotary = machineConfiguration.getAxisU(); // C-axis + var startPt = rotary.getAxisRotation(startC).multiply(new Vector(startX, 0, 0)); + var endPt = rotary.getAxisRotation(endC).multiply(new Vector(endX, 0, 0)); + + var testX = startX + (endX - startX) / 2; // interpolate as the machine + var testC = startC + (endC - startC) / 2; + var testPt = rotary.getAxisRotation(testC).multiply(new Vector(testX, 0, 0)); + + var delta = Vector.diff(endPt, startPt); + var distf = pointLineDistance(startPt, endPt, testPt); + + if (distf > maximumDistance) { + return false; // out of tolerance + } else { + return true; + } +} + +function rewindTable(startXYZ, currentZ, rewindC, feed, retract) { + if (!cFormat.areDifferent(rewindC, cOutput.getCurrent())) { + error(localize("Rewind position not found.")); + return; + } + writeComment("Rewind of C-axis, make sure retracting is possible."); + onCommand(COMMAND_STOP); + if (retract) { + writeBlock(gMotionModal.format(1), zOutput.format(currentSection.getInitialPosition().z), getFeed(feed)); + } + writeBlock(getCode("DISENGAGE_C_AXIS")); + writeBlock(getCode("ENGAGE_C_AXIS")); + gMotionModal.reset(); + xOutput.reset(); + startSpindle(false); + if (retract) { + var x = getModulus(startXYZ.x, startXYZ.y); + if (properties.rapidRewinds) { + writeBlock(gMotionModal.format(1), xOutput.format(x), getFeed(highFeedrate)); + writeBlock(gMotionModal.format(0), cOutput.format(rewindC)); + } else { + writeBlock(gMotionModal.format(1), xOutput.format(x), cOutput.format(rewindC), getFeed(highFeedrate)); + } + writeBlock(gMotionModal.format(1), zOutput.format(startXYZ.z), getFeed(feed)); + } + setCoolant(tool.coolant); + forceRewind = false; + writeComment("End of rewind"); +} + +function onLinear(_x, _y, _z, feed) { + if (machineState.useXZCMode) { + if (pendingRadiusCompensation >= 0) { + error(subst(localize("Radius compensation is not supported for operation \"%1\". You have to use G112 mode for radius compensation."), getOperationComment())); + return; + } + if (maximumCircularSweep > toRad(179)) { + error(localize("Maximum circular sweep must be below 179 degrees.")); + return; + } + + var localTolerance = getTolerance() / 4; + + var startXYZ = getCurrentPosition(); + var startX = getModulus(startXYZ.x, startXYZ.y); + var startZ = startXYZ.z; + var startC = cOutput.getCurrent(); + + var endXYZ = new Vector(_x, _y, _z); + var endX = getModulus(endXYZ.x, endXYZ.y); + var endZ = endXYZ.z; + var endC = getCWithinRange(endXYZ.x, endXYZ.y, startC); + + var currentXYZ = endXYZ; var currentX = endX; var currentZ = endZ; var currentC = endC; + var centerXYZ = machineConfiguration.getAxisU().getOffset(); + + var refined = true; + var crossingRotary = false; + forceOptimized = false; // tool tip is provided to DPM calculations + while (refined) { // stop if we dont refine + // check if we cross center of rotary axis + var _start = new Vector(startXYZ.x, startXYZ.y, 0); + var _current = new Vector(currentXYZ.x, currentXYZ.y, 0); + var _center = new Vector(centerXYZ.x, centerXYZ.y, 0); + if ((xFormat.getResultingValue(pointLineDistance(_start, _current, _center)) == 0) && + (xFormat.getResultingValue(Vector.diff(_start, _center).length) != 0) && + (xFormat.getResultingValue(Vector.diff(_current, _center).length) != 0)) { + var ratio = Vector.diff(_center, _start).length / Vector.diff(_current, _start).length; + currentXYZ = centerXYZ; + currentXYZ.z = startZ + (endZ - startZ) * ratio; + currentX = getModulus(currentXYZ.x, currentXYZ.y); + currentZ = currentXYZ.z; + currentC = startC; + crossingRotary = true; + } else { // check if move is out of tolerance + refined = false; + while (!refineSegmentXC(startX, startC, currentX, currentC, localTolerance)) { // move is out of tolerance + refined = true; + currentXYZ = Vector.lerp(startXYZ, currentXYZ, 0.75); + currentX = getModulus(currentXYZ.x, currentXYZ.y); + currentZ = currentXYZ.z; + currentC = getCWithinRange(currentXYZ.x, currentXYZ.y, startC); + if (Vector.diff(startXYZ, currentXYZ).length < 1e-5) { // back to start point, output error + if (forceRewind) { + break; + } else { + warning(localize("Linear move cannot be mapped to rotary XZC motion.")); + break; + } + } + } + } + + currentC = getCWithinRange(currentXYZ.x, currentXYZ.y, startC); + if (forceRewind) { + var rewindC = getCClosest(startXYZ.x, startXYZ.y, currentC); + xOutput.reset(); // force X for repositioning + rewindTable(startXYZ, currentZ, rewindC, feed, true); + previousABC.setZ(rewindC); + } + var x = xOutput.format(currentX); + var c = cOutput.format(currentC); + var z = zOutput.format(currentZ); + var actualFeed = getMultiaxisFeed(currentXYZ.x, currentXYZ.y, currentXYZ.z, 0, 0, currentC, feed); + if (x || c || z) { + writeBlock(gMotionModal.format(1), x, c, z, getFeed(actualFeed.frn)); + } + setCurrentPosition(currentXYZ); + previousABC.setZ(currentC); + if (crossingRotary) { + writeBlock(gMotionModal.format(1), cOutput.format(endC), getFeed(feed)); // rotate at X0 with endC + previousABC.setZ(endC); + forceFeed(); + } + startX = currentX; startZ = currentZ; startC = crossingRotary ? endC : currentC; startXYZ = currentXYZ; // loop start point + currentX = endX; currentZ = endZ; currentC = endC; currentXYZ = endXYZ; // loop end point + crossingRotary = false; + } + forceOptimized = undefined; + return; + } + + if (isSpeedFeedSynchronizationActive()) { + resetFeed = true; + var threadPitch = getParameter("operation:threadPitch"); + var threadsPerInch = 1.0 / threadPitch; // per mm for metric + writeBlock(gMotionModal.format(32), xOutput.format(_x), yOutput.format(_y), zOutput.format(_z), pitchOutput.format(1 / threadsPerInch)); + return; + } + if (resetFeed) { + resetFeed = false; + forceFeed(); + } + var x = xOutput.format(_x); + var y = yOutput.format(_y); + var z = zOutput.format(_z); + var f = ((currentSection.feedMode != FEED_PER_REVOLUTION) && machineState.feedPerRevolution) ? feedOutput.format(feed / spindleSpeed) : getFeed(feed); + if (x || y || z) { + if (pendingRadiusCompensation >= 0) { + pendingRadiusCompensation = -1; + writeBlock(gPlaneModal.format(getPlane())); + if (getMachiningDirection(currentSection) == MACHINING_DIRECTION_INDEXING) { + error(localize("Tool orientation is not supported for radius compensation.")); + return; + } + 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 onRapid5D(_x, _y, _z, _a, _b, _c) { + if (!currentSection.isOptimizedForMachine()) { + error(localize("Multi-axis motion is not supported for XZC mode.")); + 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(getB(new Vector(_a, _b, _c), currentSection)); + var c = cOutput.format(_c); + if (true) { + // axes are not synchronized + writeBlock(gMotionModal.format(1), x, y, z, a, b, c, getFeed(highFeedrate)); + } else { + writeBlock(gMotionModal.format(0), x, y, z, a, b, c); + forceFeed(); + } + previousABC.setZ(_c); +} + +function onLinear5D(_x, _y, _z, _a, _b, _c, feed) { + if (!currentSection.isOptimizedForMachine()) { + error(localize("Multi-axis motion is not supported for XZC mode.")); + 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(getB(new Vector(_a, _b, _c), currentSection)); + var c = cOutput.format(_c); + + var actualFeed = getMultiaxisFeed(_x, _y, _z, _a, _b, _c, feed); + var f = getFeed(actualFeed.frn); + + if (x || y || z || a || b || c) { + writeBlock(gMotionModal.format(1), x, y, z, a, b, c, f); + } else if (f) { + if (getNextRecord().isMotion()) { // try not to output feed without motion + forceFeed(); // force feed on next line + } else { + writeBlock(gMotionModal.format(1), f); + } + } + previousABC.setZ(_c); +} + +// Start of multi-axis feedrate logic +/***** Be sure to add 'useInverseTime' to post properties if necessary. *****/ +/***** 'inverseTimeOutput' should be defined if Inverse Time feedrates are supported. *****/ +/***** '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 = 45000; // maximum value to output for Inverse Time feeds +var maxDPM = 99999; // maximum value to output for DPM feeds +var useInverseTimeFeed = false; // use DPM feeds +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 (useInverseTimeFeed) { // 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 = !getOptimizedMode() && (forceOptimized == undefined); // set to false for rotary heads + 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 (false) { // 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 + var feedRate = _feed / (_moveLength.radius.z / (toPreciseUnit(properties.setting102, IN) / 2.0)); + feedRate = Math.min(feedRate, highFeedrate / 2); + dpmFeed = Math.min(feedRate, maxDPM); + if (Math.abs(dpmFeed - previousDPMFeed) < dpmFeedToler) { + dpmFeed = previousDPMFeed; + } + } + 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 + +function onCircular(clockwise, cx, cy, cz, x, y, z, feed) { + var toler = (machineState.useXZCMode || machineState.usePolarMode) ? getTolerance() / 2 : getTolerance(); + if (machineState.useXZCMode) { + switch (getCircularPlane()) { + case PLANE_ZX: + if (!isSpiral()) { + var c = getCClosest(x, y, cOutput.getCurrent()); + if (!cFormat.areDifferent(c, cOutput.getCurrent())) { + validate(getCircularSweep() < Math.PI, localize("Circular sweep exceeds limit.")); + var start = getCurrentPosition(); + writeBlock(gPlaneModal.format(18), gMotionModal.format(clockwise ? 2 : 3), xOutput.format(getModulus(x, y)), cOutput.format(c), zOutput.format(z), iOutput.format(cx - start.x, 0), kOutput.format(cz - start.z, 0), getFeed(feed)); + previousABC.setZ(c); + return; + } + } + break; + case PLANE_XY: + var d2 = center.x * center.x + center.y * center.y; + if (d2 < 1e-6) { // center is on rotary axis + var c = getCWithinRange(x, y, cOutput.getCurrent(), !clockwise); + if (!forceRewind) { + var actualFeed = getMultiaxisFeed(x, y, z, 0, 0, c, feed); + writeBlock(gMotionModal.format(1), xOutput.format(getModulus(x, y)), cOutput.format(c), zOutput.format(z), getFeed(actualFeed.frn)); + previousABC.setZ(c); + return; + } + } + break; + } + + linearize(toler); + return; + } + + if (machineState.usePolarMode && !usePolarCircular) { + linearize(toler); + return; + } + + 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(); + + if (isFullCircle()) { + if (properties.useRadius || isHelical()) { // radius mode does not support full arcs + linearize(toler); + return; + } + switch (getCircularPlane()) { + case PLANE_XY: + writeBlock(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: + if (machineState.usePolarMode) { + linearize(tolerance); + return; + } + writeBlock(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: + if (machineState.usePolarMode) { + linearize(tolerance); + return; + } + writeBlock(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(toler); + } + } else if (!properties.useRadius) { + if (isHelical() && ((getCircularSweep() < toRad(30)) || (getHelicalPitch() > 10))) { // avoid G112 issue + linearize(toler); + return; + } + switch (getCircularPlane()) { + case PLANE_XY: + if (!xFormat.isSignificant(start.x) && machineState.usePolarMode) { + linearize(toler); // avoid G112 issues + return; + } + writeBlock(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: + if (machineState.usePolarMode) { + linearize(tolerance); + return; + } + writeBlock(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: + if (machineState.usePolarMode) { + linearize(tolerance); + return; + } + writeBlock(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(toler); + } + } else { // use radius mode + if (isHelical() && ((getCircularSweep() < toRad(30)) || (getHelicalPitch() > 10))) { + linearize(toler); + return; + } + var r = getCircularRadius(); + if (toDeg(getCircularSweep()) > (180 + 1e-9)) { + r = -r; // allow up to <360 deg arcs + } + switch (getCircularPlane()) { + case PLANE_XY: + if (!xFormat.isSignificant(start.x) && machineState.usePolarMode) { + linearize(toler); // avoid G112 issues + return; + } + writeBlock(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: + if (machineState.usePolarMode) { + linearize(tolerance); + return; + } + writeBlock(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: + if (machineState.usePolarMode) { + linearize(tolerance); + return; + } + writeBlock(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(toler); + } + } +} + +function onCycle() { + if ((typeof isSubSpindleCycle == "function") && isSubSpindleCycle(cycleType)) { + if (!properties.gotSecondarySpindle) { + error(localize("Secondary spindle is not available.")); + return; + } + + writeln(""); + if (hasParameter("operation-comment")) { + var comment = getParameter("operation-comment"); + if (comment) { + writeComment(comment); + } + } + + if (!machineState.stockTransferIsActive) { + setCoolant(COOLANT_OFF); + writeRetract(currentSection, false); // no retract in Z + + // wcs required here + currentWorkOffset = undefined; + writeWCS(currentSection); + + // preload next cutting tool + preloadCutoffTool(); + } + + switch (cycleType) { + case "secondary-spindle-grab": + if (cycle.usePartCatcher) { + engagePartCatcher(true); + } + writeBlock(getCode("FEED_MODE_UNIT_MIN")); // mm/rev + if (cycle.stopSpindle) { // no spindle rotation + writeBlock(getCode("STOP_SPINDLE")); + } else { // spindle rotation + writeBlock(sOutput.format(cycle.spindleSpeed), tool.clockwise ? getCode("START_MAIN_SPINDLE_CW") : getCode("START_MAIN_SPINDLE_CCW")); + writeBlock(pOutput.format(cycle.spindleSpeed), tool.clockwise ? getCode("START_SUB_SPINDLE_CCW") : getCode("START_SUB_SPINDLE_CW")); // inverted + } + writeBlock(getCode("SPINDLE_SYNCHRONIZATION_ON"), "R" + abcFormat.format(cycle.spindleOrientation), formatComment("SPINDLE SYNCHRONIZATION ON")); // Sync spindles + if (properties.airCleanChuck) { + writeBlock(getCode("MAINSPINDLE_AIR_BLAST_ON"), formatComment("MAINSPINDLE AIR BLAST ON")); + writeBlock(getCode("SUBSPINDLE_AIR_BLAST_ON"), formatComment("SUBSPINDLE AIR BLAST ON")); + } + writeBlock( + getCode(currentSection.spindle == SPINDLE_PRIMARY ? "UNCLAMP_SECONDARY_CHUCK" : "UNCLAMP_PRIMARY_CHUCK"), + formatComment(currentSection.spindle == SPINDLE_PRIMARY ? "UNCLAMP SECONDARY CHUCK" : "UNCLAMP PRIMARY CHUCK") + ); + onDwell(cycle.dwell); + gMotionModal.reset(); + writeBlock(conditional(cycle.useMachineFrame, gFormat.format(53)), gMotionModal.format(0), "B" + spatialFormat.format(cycle.feedPosition)); + if (properties.airCleanChuck) { + writeBlock(getCode("MAINSPINDLE_AIR_BLAST_OFF"), formatComment("MAINSPINDLE AIR BLAST OFF")); + writeBlock(getCode("SUBSPINDLE_AIR_BLAST_OFF"), formatComment("SUBSPINDLE AIR BLAST OFF")); + } + + onDwell(cycle.dwell); + writeBlock(conditional(cycle.useMachineFrame, gFormat.format(53)), gMotionModal.format(1), "B" + spatialFormat.format(cycle.chuckPosition), getFeed(cycle.feedrate)); + writeBlock( + getCode(currentSection.spindle == SPINDLE_PRIMARY ? "CLAMP_SECONDARY_CHUCK" : "CLAMP_PRIMARY_CHUCK"), + formatComment(currentSection.spindle == SPINDLE_PRIMARY ? "CLAMP SECONDARY CHUCK" : "CLAMP PRIMARY CHUCK") + ); + onDwell(cycle.dwell); + machineState.stockTransferIsActive = true; + break; + case "secondary-spindle-return": + // determine if pull operation, spindle return, or both + var secondaryPull = false; + var secondaryHome = false; + + // pull part only (when offset!=0), Return secondary spindle to home (when offset=0) + var feedDis = 0; + var feedPosition = cycle.feedPosition; + if (cycle.useMachineFrame == 1) { + if (hasParameter("operation:feedPlaneHeight_direct")) { // Inventor + feedDis = getParameter("operation:feedPlaneHeight_direct"); + } else if (hasParameter("operation:feedPlaneHeightDirect")) { // HSMWorks + feedDis = getParameter("operation:feedPlaneHeightDirect"); + } + feedPosition = feedDis; + } else if (hasParameter("operation:feedPlaneHeight_offset")) { // Inventor + feedDis = getParameter("operation:feedPlaneHeight_offset"); + } else if (hasParameter("operation:feedPlaneHeightOffset")) { // HSMWorks + feedDis = getParameter("operation:feedPlaneHeightOffset"); + } + // Transfer part to secondary spindle + if (cycle.unclampMode != "keep-clamped") { + secondaryPull = feedDis != 0; + secondaryHome = true; + } else { + // pull part only (when offset!=0), Return secondary spindle to home (when offset=0) + secondaryPull = feedDis != 0; + secondaryHome = !secondaryPull; + } + + if (!machineState.stockTransferIsActive) { + writeBlock(getCode("FEED_MODE_UNIT_REV")); // mm/rev + if (cycle.stopSpindle) { // no spindle rotation + writeBlock(getCode("STOP_SPINDLE")); + } else { // spindle rotation + writeBlock(sOutput.format(cycle.spindleSpeed), tool.clockwise ? getCode("START_MAIN_SPINDLE_CW") : getCode("START_MAIN_SPINDLE_CCW")); + writeBlock(pOutput.format(cycle.spindleSpeed), tool.clockwise ? getCode("START_SUB_SPINDLE_CCW") : getCode("START_SUB_SPINDLE_CW")); // inverted + } + writeBlock(getCode("SPINDLE_SYNCHRONIZATION_ON"), formatComment("SPINDLE SYNCHRONIZATION ON")); // Sync spindles + } + + if (secondaryPull) { + writeBlock(getCode("UNCLAMP_PRIMARY_CHUCK"), formatComment("UNCLAMP PRIMARY CHUCK")); + onDwell(cycle.dwell); + writeBlock(conditional(cycle.useMachineFrame, gFormat.format(53)), gMotionModal.format(1), "B" + spatialFormat.format(cycle.feedPosition), getFeed(cycle.feedrate)); + } + if (secondaryHome) { + setCoolant(COOLANT_OFF); + if (cycle.unclampMode == "unclamp-secondary") { // leave part in main spindle + writeBlock(getCode("CLAMP_PRIMARY_CHUCK"), formatComment("CLAMP PRIMARY CHUCK")); + onDwell(cycle.dwell); + writeBlock(getCode("UNCLAMP_SECONDARY_CHUCK"), formatComment("UNCLAMP SECONDARY CHUCK")); + onDwell(cycle.dwell); + } else if (cycle.unclampMode == "unclamp-primary") { + if (!secondaryPull) { + writeBlock(getCode("UNCLAMP_PRIMARY_CHUCK"), formatComment("UNCLAMP PRIMARY CHUCK")); + onDwell(cycle.dwell); + } + } + writeBlock(gMotionModal.format(0), gFormat.format(53), "B" + spatialFormat.format(properties.g53WorkPositionSub)); + if (machineState.spindleSynchronizationIsActive) { // spindles are synchronized + writeBlock(getCode("SPINDLE_SYNCHRONIZATION_OFF"), formatComment("SPINDLE SYNCHRONIZATION OFF")); // disable spindle sync + } + } else { + writeBlock(getCode("CLAMP_PRIMARY_CHUCK"), formatComment("CLAMP PRIMARY CHUCK")); + onDwell(cycle.dwell); + } + machineState.stockTransferIsActive = true; + break; + } + } +} + +var saveShowSequenceNumbers = true; +var xyzFormat = createFormat({decimals:(unit == MM ? 4 : 5), forceDecimal:true}); +var pathBlockNumber = {start: 0, end: 0}; +var isCannedCycle = false; + +function onCyclePath() { + saveShowSequenceNumbers = properties.showSequenceNumbers; + isCannedCycle = true; + // buffer all paths and stop feeds being output + feedOutput.disable(); + properties.showSequenceNumbers = false; + redirectToBuffer(); + gMotionModal.reset(); + if ((hasParameter("operation:grooving") && getParameter("operation:grooving").toUpperCase() != "OFF")) { + xOutput.reset(); + zOutput.reset(); + } +} + +function onCyclePathEnd() { + properties.showSequenceNumbers = saveShowSequenceNumbers; // reset property to initial state + feedOutput.enable(); + var cyclePath = String(getRedirectionBuffer()).split(EOL); // get cycle path from buffer + closeRedirection(); + for (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), + "P" + (getStartEndSequenceNumber(cyclePath, true)), + "Q" + (getStartEndSequenceNumber(cyclePath, false)), + "U" + xFormat.format(cycle.xStockToLeave), + "W" + xyzFormat.format(cycle.zStockToLeave), + "D" + xyzFormat.format(cycle.depthOfCut), + 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 + properties.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 + properties.showSequenceNumbers = saveShowSequenceNumbers; // reset property to initial state + isCannedCycle = false; + } +} + +function getStartEndSequenceNumber(cyclePath, start) { + if (start) { + pathBlockNumber.start = sequenceNumber + conditional(saveShowSequenceNumbers, properties.sequenceNumberIncrement); + return pathBlockNumber.start; + } else { + pathBlockNumber.end = sequenceNumber + properties.sequenceNumberIncrement + conditional(saveShowSequenceNumbers, (cyclePath.length - 1) * properties.sequenceNumberIncrement); + return pathBlockNumber.end; + } +} + +function getCommonCycle(x, y, z, r) { + // forceXYZ(); // force xyz on first drill hole of any cycle + if (machineState.useXZCMode) { + cOutput.reset(); + return [xOutput.format(getModulus(x, y)), cOutput.format(getCWithinRange(x, y, cOutput.getCurrent())), + zOutput.format(z), + (r !== undefined) ? ("R" + spatialFormat.format((gPlaneModal.getCurrent() == 19) ? r * 2 : r)) : ""]; + } else { + return [xOutput.format(x), yOutput.format(y), + zOutput.format(z), + (r !== undefined) ? ("R" + spatialFormat.format((gPlaneModal.getCurrent() == 19) ? r * 2 : r)) : ""]; + } +} + +function writeCycleClearance() { + if (true) { + switch (gPlaneModal.getCurrent()) { + case 18: + writeBlock(gMotionModal.format(0), zOutput.format(cycle.clearance)); + break; + case 19: + writeBlock(gMotionModal.format(0), xOutput.format(cycle.clearance)); + break; + default: + error(localize("Unsupported drilling orientation.")); + return; + } + } +} + +var threadStart; +var threadEnd; +function moveToThreadStart(x, y, z) { + var cuttingAngle = 0; + if (hasParameter("operation:infeedAngle")) { + cuttingAngle = getParameter("operation:infeedAngle"); + } + if (cuttingAngle != 0) { + var zz; + if (isFirstCyclePoint()) { + threadStart = getCurrentPosition(); + threadEnd = new Vector(x, y, z); + } else { + var zz = threadStart.z - (Math.abs(threadEnd.x - x) * Math.tan(toRad(cuttingAngle))); + writeBlock(gMotionModal.format(0), zOutput.format(zz)); + threadStart.setZ(zz); + threadEnd = new Vector(x, y, z); + } + } +} + +function onCyclePoint(x, y, z) { + + if (!properties.useCycles || currentSection.isMultiAxis() || getMachiningDirection(currentSection) == MACHINING_DIRECTION_INDEXING) { + expandCyclePoint(x, y, z); + return; + } + writeBlock(gPlaneModal.format(getPlane())); + + var gCycleTapping; + switch (cycleType) { + case "tapping-with-chip-breaking": + case "right-tapping": + case "left-tapping": + case "tapping": + if (gPlaneModal.getCurrent() == 19) { // radial + if (tool.type == TOOL_TAP_LEFT_HAND) { + gCycleTapping = 196; + } else { + gCycleTapping = 195; + } + } else { // axial + if (tool.type == TOOL_TAP_LEFT_HAND) { + gCycleTapping = machineState.axialCenterDrilling ? 184 : 186; + } else { + gCycleTapping = machineState.axialCenterDrilling ? 84 : 95; + } + } + break; + } + + switch (cycleType) { + case "thread-turning": + if (properties.useSimpleThread || + (hasParameter("operation:doMultipleThreads") && (getParameter("operation:doMultipleThreads") != 0)) || + (hasParameter("operation:infeedMode") && (getParameter("operation:infeedMode") != "constant"))) { + var i = -cycle.incrementalX; // positive if taper goes down - delta radius + moveToThreadStart(x, y, z); + xOutput.reset(); + zOutput.reset(); + writeBlock( + gMotionModal.format(92), + xOutput.format(x - cycle.incrementalX), + yOutput.format(y), + zOutput.format(z), + conditional(zFormat.isSignificant(i), g76IOutput.format(i)), + pitchOutput.format(cycle.pitch) + ); + } else { + if (isLastCyclePoint()) { + // thread height and depth of cut + var threadHeight = getParameter("operation:threadDepth"); + var firstDepthOfCut = threadHeight / getParameter("operation:numberOfStepdowns"); + var repeatPass = hasParameter("operation:nullPass") ? getParameter("operation:nullPass") : 0; + var cuttingAngle = 59; // Angle is not stored with tool. toDeg(tool.getTaperAngle()); + if (hasParameter("operation:infeedAngle")) { + cuttingAngle = getParameter("operation:infeedAngle") * 2; + } + var i = -cycle.incrementalX; // positive if taper goes down - delta radius + gCycleModal.reset(); + + // threading cycle + writeBlock( + gCycleModal.format(76), + xOutput.format(x - cycle.incrementalX), + zOutput.format(z), + g76KOutput.format(threadHeight), + g76DOutput.format(firstDepthOfCut), + g76AOutput.format(cuttingAngle), + // "P2" // P1 = Single edge-cutting constant, P2 = Double edge-cutting constant, P3 = Single-depth constant, P4 = Double-depth constant + pitchOutput.format(cycle.pitch) + ); + } + } + gMotionModal.reset(); + forceFeed(); + return; + } + if (true) { + if (gPlaneModal.getCurrent() == 17) { + error(localize("Drilling in G17 is not supported.")); + return; + } + // repositionToCycleClearance(cycle, x, y, z); + // return to initial Z which is clearance plane and set absolute mode + feedOutput.reset(); + + var F = (machineState.feedPerRevolution ? cycle.feedrate / spindleSpeed : cycle.feedrate); + var P = (cycle.dwell == 0) ? 0 : clamp(1, cycle.dwell * 1000, 99999999); // in milliseconds + + switch (cycleType) { + case "drilling": + forceXYZ(); + writeCycleClearance(); + writeBlock( + gCycleModal.format(gPlaneModal.getCurrent() == 19 ? 241 : 81), + getCommonCycle(x, y, z, cycle.retract), + feedOutput.format(F) + ); + break; + case "counter-boring": + writeCycleClearance(); + forceXYZ(); + if (P > 0) { + writeBlock( + gCycleModal.format(gPlaneModal.getCurrent() == 19 ? 242 : 82), + getCommonCycle(x, y, z, cycle.retract), + "P" + milliFormat.format(P), + feedOutput.format(F) + ); + } else { + writeBlock( + gCycleModal.format(gPlaneModal.getCurrent() == 19 ? 241 : 81), + getCommonCycle(x, y, z, cycle.retract), + feedOutput.format(F) + ); + } + break; + case "chip-breaking": + case "deep-drilling": + if (cycleType == "chip-breaking" && (cycle.accumulatedDepth < cycle.depth)) { + expandCyclePoint(x, y, z); + return; + } else { + writeCycleClearance(); + forceXYZ(); + writeBlock( + gCycleModal.format(gPlaneModal.getCurrent() == 19 ? 243 : 83), + getCommonCycle(x, y, z, cycle.retract), + "Q" + spatialFormat.format(cycle.incrementalDepth), // lathe prefers single Q peck value, IJK causes error + // "I" + spatialFormat.format(cycle.incrementalDepth), + // "J" + spatialFormat.format(cycle.incrementalDepthReduction), + // "K" + spatialFormat.format(cycle.minimumIncrementalDepth), + conditional(P > 0, "P" + milliFormat.format(P)), + feedOutput.format(F) + ); + } + break; + case "tapping": + writeCycleClearance(); + if (gPlaneModal.getCurrent() == 19) { + xOutput.reset(); + writeBlock(gMotionModal.format(0), zOutput.format(z), yOutput.format(y)); + writeBlock(gMotionModal.format(0), xOutput.format(cycle.retract)); + writeBlock( + gCycleModal.format(gCycleTapping), + getCommonCycle(x, y, z, undefined), + pitchOutput.format(F) + ); + } else { + forceXYZ(); + writeBlock( + gCycleModal.format(gCycleTapping), + getCommonCycle(x, y, z, cycle.retract), + pitchOutput.format(F) + ); + } + forceFeed(); + break; + case "left-tapping": + writeCycleClearance(); + xOutput.reset(); + if (gPlaneModal.getCurrent() == 19) { + writeBlock(gMotionModal.format(0), zOutput.format(z), yOutput.format(y)); + writeBlock(gMotionModal.format(0), xOutput.format(cycle.retract)); + } + writeBlock( + gCycleModal.format(gCycleTapping), + getCommonCycle(x, y, z, (gPlaneModal.getCurrent() == 19) ? undefined : cycle.retract), + pitchOutput.format(F) + ); + forceFeed(); + break; + case "right-tapping": + writeCycleClearance(); + xOutput.reset(); + if (gPlaneModal.getCurrent() == 19) { + writeBlock(gMotionModal.format(0), zOutput.format(z), yOutput.format(y)); + writeBlock(gMotionModal.format(0), xOutput.format(cycle.retract)); + } + writeBlock( + gCycleModal.format(gCycleTapping), + getCommonCycle(x, y, z, (gPlaneModal.getCurrent() == 19) ? undefined : cycle.retract), + pitchOutput.format(F) + ); + forceFeed(); + break; + case "tapping-with-chip-breaking": + writeCycleClearance(); + xOutput.reset(); + if (gPlaneModal.getCurrent() == 19) { + writeBlock(gMotionModal.format(0), zOutput.format(z), yOutput.format(y)); + writeBlock(gMotionModal.format(0), xOutput.format(cycle.retract)); + } + + // Parameter 57 bit 6, REPT RIG TAP, is set to 1 (On) + // On Mill software versions12.09 and above, REPT RIG TAP has been moved from the Parameters to Setting 133 + warningOnce(localize("For tapping with chip breaking make sure REPT RIG TAP (Setting 133) is enabled on your Haas."), WARNING_REPEAT_TAPPING); + + var u = cycle.stock; + var step = cycle.incrementalDepth; + var first = true; + + while (u > cycle.bottom) { + if (step < cycle.minimumIncrementalDepth) { + step = cycle.minimumIncrementalDepth; + } + u -= step; + step -= cycle.incrementalDepthReduction; + gCycleModal.reset(); // required + u = Math.max(u, cycle.bottom); + if (first) { + first = false; + writeBlock( + gCycleModal.format(gCycleTapping), + getCommonCycle((gPlaneModal.getCurrent() == 19) ? u : x, y, (gPlaneModal.getCurrent() == 19) ? z : u, (gPlaneModal.getCurrent() == 19) ? undefined : cycle.retract), + pitchOutput.format(F) + ); + } else { + writeBlock( + gCycleModal.format(gCycleTapping), + conditional(gPlaneModal.getCurrent() == 18, "Z" + spatialFormat.format(u)), + conditional(gPlaneModal.getCurrent() == 19, "X" + xFormat.format(u)), + pitchOutput.format(F) + ); + } + } + forceFeed(); + break; + case "fine-boring": + expandCyclePoint(x, y, z); + break; + case "reaming": + if (gPlaneModal.getCurrent() == 19) { + expandCyclePoint(x, y, z); + } else { + writeCycleClearance(); + forceXYZ(); + writeBlock( + gCycleModal.format(85), + getCommonCycle(x, y, z, cycle.retract), + feedOutput.format(F) + ); + } + break; + case "stop-boring": + if (P > 0) { + expandCyclePoint(x, y, z); + } else { + writeCycleClearance(); + forceXYZ(); + writeBlock( + gCycleModal.format((gPlaneModal.getCurrent() == 19) ? 246 : 86), + getCommonCycle(x, y, z, cycle.retract), + feedOutput.format(F) + ); + } + break; + case "boring": + if (P > 0) { + expandCyclePoint(x, y, z); + } else { + writeCycleClearance(); + forceXYZ(); + writeBlock( + gCycleModal.format((gPlaneModal.getCurrent() == 19) ? 245 : 85), + getCommonCycle(x, y, z, cycle.retract), + feedOutput.format(F) + ); + } + break; + default: + expandCyclePoint(x, y, z); + } + if (!cycleExpanded) { + writeBlock(gCycleModal.format(80)); + gMotionModal.reset(); + } + } else { + if (cycleExpanded) { + expandCyclePoint(x, y, z); + } else if (machineState.useXZCMode) { + var _x = xOutput.format(getModulus(x, y)); + var _c = cOutput.format(getCWithinRange(x, y, cOutput.getCurrent())); + if (!_x /*&& !_y*/ && !_c) { + xOutput.reset(); // at least one axis is required + _x = xOutput.format(getModulus(x, y)); + } + writeBlock(_x, _c); + } else { + var _x = xOutput.format(x); + var _y = yOutput.format(y); + var _z = zOutput.format(z); + if (!_x && !_y && !_z) { + switch (gPlaneModal.getCurrent()) { + case 18: // ZX + xOutput.reset(); // at least one axis is required + yOutput.reset(); // at least one axis is required + _x = xOutput.format(x); + _y = yOutput.format(y); + break; + case 19: // YZ + yOutput.reset(); // at least one axis is required + zOutput.reset(); // at least one axis is required + _y = yOutput.format(y); + _z = zOutput.format(z); + break; + } + } + writeBlock(_x, _y, _z); + } + } +} + +function onCycleEnd() { + if (!cycleExpanded && !machineState.stockTransferIsActive && (typeof isSubSpindleCycle == "function") && isSubSpindleCycle(cycleType)) { + switch (cycleType) { + case "thread-turning": + forceFeed(); + xOutput.reset(); + zOutput.reset(); + g76IOutput.reset(); + break; + default: + writeBlock(gCycleModal.format(80)); + gMotionModal.reset(); + } + } +} + +function onPassThrough(text) { + writeBlock(text); +} + +function onParameter(name, value) { + if (name == "action") { + if (String(value).toUpperCase() == "USEPOLARMODE") { + forcePolarMode = true; + } else if (String(value).toUpperCase() == "PARTEJECT") { + ejectRoutine = true; + } + } +} + +var currentCoolantMode = COOLANT_OFF; + +function setCoolant(coolant) { + var optionalCoolant = false; + if (coolant == currentCoolantMode) { + if (operationNeedsSafeStart) { + optionalCoolant = true; + } else { + return; // coolant is already active + } + } + + var m = undefined; + if (coolant == COOLANT_OFF) { + if (currentCoolantMode == COOLANT_THROUGH_TOOL) { + skipBlock = optionalCoolant; + writeBlock(getCode("COOLANT_THROUGH_TOOL_OFF")); + } else if (currentCoolantMode == COOLANT_AIR) { + skipBlock = optionalCoolant; + writeBlock(getCode("COOLANT_AIR_OFF")); + } else { + skipBlock = optionalCoolant; + writeBlock(getCode("COOLANT_OFF")); + } + currentCoolantMode = COOLANT_OFF; + return; + } + + switch (coolant) { + case COOLANT_FLOOD: + m = getCode("COOLANT_FLOOD_ON"); + break; + case COOLANT_THROUGH_TOOL: + m = getCode("COOLANT_THROUGH_TOOL_ON"); + break; + case COOLANT_AIR: + m = getCode("COOLANT_AIR_ON"); + break; + default: + onUnsupportedCoolant(coolant); + m = getCode("COOLANT_OFF"); + } + + if (m) { + skipBlock = optionalCoolant; + writeBlock(m); + currentCoolantMode = coolant; + } +} + +function onCommand(command) { + switch (command) { + case COMMAND_COOLANT_OFF: + setCoolant(COOLANT_OFF); + break; + case COMMAND_COOLANT_ON: + setCoolant(tool.coolant); + break; + case COMMAND_START_SPINDLE: + if (machineState.isTurningOperation || machineState.axialCenterDrilling) { + if (currentSection.spindle == SPINDLE_PRIMARY) { + writeBlock(tool.clockwise ? getCode("START_MAIN_SPINDLE_CW") : getCode("START_MAIN_SPINDLE_CCW")); + } else { + writeBlock(tool.clockwise ? getCode("START_SUB_SPINDLE_CW") : getCode("START_SUB_SPINDLE_CCW")); + } + } else { + writeBlock(tool.clockwise ? getCode("START_LIVE_TOOL_CW") : getCode("START_LIVE_TOOL_CCW")); + } + break; + case COMMAND_STOP_SPINDLE: + if (properties.useSSV) { + writeBlock(ssvModal.format(39)); + } + writeBlock(getCode("STOP_SPINDLE")); + break; + case COMMAND_LOCK_MULTI_AXIS: + writeBlock(getCode((currentSection.spindle == SPINDLE_PRIMARY) ? "MAIN_SPINDLE_BRAKE_ON" : "SUB_SPINDLE_BRAKE_ON")); + break; + case COMMAND_UNLOCK_MULTI_AXIS: + writeBlock(getCode((currentSection.spindle == SPINDLE_PRIMARY) ? "MAIN_SPINDLE_BRAKE_OFF" : "SUB_SPINDLE_BRAKE_OFF")); + break; + case COMMAND_START_CHIP_TRANSPORT: + writeBlock(getCode("START_CHIP_TRANSPORT")); + break; + case COMMAND_STOP_CHIP_TRANSPORT: + writeBlock(getCode("STOP_CHIP_TRANSPORT")); + break; + case COMMAND_OPEN_DOOR: + if (gotDoorControl) { + writeBlock(getCode("OPEN_DOOR")); // optional + } + break; + case COMMAND_CLOSE_DOOR: + if (gotDoorControl) { + writeBlock(getCode("CLOSE_DOOR")); // optional + } + 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: + if (!skipBlock) { + forceSpindleSpeed = true; + currentCoolantMode = undefined; + } + writeBlock(mFormat.format(0)); + break; + case COMMAND_OPTIONAL_STOP: + if (!skipBlock) { + forceSpindleSpeed = true; + currentCoolantMode = undefined; + } + writeBlock(mFormat.format(1)); + break; + case COMMAND_END: + writeBlock(mFormat.format(2)); + break; + case COMMAND_ORIENTATE_SPINDLE: + if (machineState.isTurningOperation) { + if (currentSection.spindle == SPINDLE_PRIMARY) { + writeBlock(mFormat.format(19)); // use P or R to set angle (optional) + } else { + writeBlock(mFormat.format(119)); + } + } else { + if (isSameDirection(currentSection.workPlane.forward, new Vector(0, 0, 1))) { + writeBlock(mFormat.format(19)); // use P or R to set angle (optional) + } else if (isSameDirection(currentSection.workPlane.forward, new Vector(0, 0, -1))) { + writeBlock(mFormat.format(119)); + } else { + error(localize("Spindle orientation is not supported for live tooling.")); + return; + } + } + break; + // case COMMAND_CLAMP: // add support for clamping + // case COMMAND_UNCLAMP: // add support for clamping + default: + onUnsupportedCommand(command); + } +} + +/** Preload cutoff tool prior to spindle transfer/cutoff. */ +var prePositionCutoffTool = true; +function preloadCutoffTool() { + if (isLastSection()) { + return; + } + var numberOfSections = getNumberOfSections(); + for (var i = getNextSection().getId(); i < numberOfSections; ++i) { + var section = getSection(i); + if (section.getParameter("operation-strategy") == "turningSecondarySpindleReturn") { + continue; + } else if (section.getType() != TYPE_TURNING || section.spindle != SPINDLE_PRIMARY) { + break; + } else if (section.getParameter("operation-strategy") == "turningPart") { + var tool = section.getTool(); + var compensationOffset = tool.compensationOffset; + writeBlock("T" + toolFormat.format(tool.number * 100 + compensationOffset)); + if (prePositionCutoffTool) { + var initialPosition = getFramePosition(section.getInitialPosition()); + writeBlock(zOutput.format(initialPosition.z)); + } + break; + } + } + return; +} + +function engagePartCatcher(engage) { + if (engage) { + // catch part here + writeBlock(getCode("PART_CATCHER_ON"), formatComment(localize("PART CATCHER ON"))); + } else { + onCommand(COMMAND_COOLANT_OFF); + if (gotYAxis) { + writeBlock(gFormat.format(53), gMotionModal.format(0), "Y" + yFormat.format(properties.g53HomePositionY)); // retract + yOutput.reset(); + } + writeBlock(gFormat.format(53), gMotionModal.format(0), "X" + xFormat.format(properties.g53HomePositionX)); // retract + writeBlock(gFormat.format(53), gMotionModal.format(0), "Z" + zFormat.format(currentSection.spindle == SPINDLE_SECONDARY ? properties.g53HomePositionSubZ : properties.g53HomePositionZ)); // retract + writeBlock(getCode("PART_CATCHER_OFF"), formatComment(localize("PART CATCHER OFF"))); + forceXYZ(); + } +} + +function ejectPart() { + writeln(""); + writeComment(localize("PART EJECT")); + + gMotionModal.reset(); + //writeBlock(gFormat.format(330)); // retract bar feeder + //goHome(); // Position all axes to home position + + if (gotYAxis) { + writeBlock(gFormat.format(53), gMotionModal.format(0), "Y" + yFormat.format(properties.g53HomePositionY)); // retract + yOutput.reset(); + } + writeBlock(gFormat.format(53), gMotionModal.format(0), "X" + xFormat.format(properties.g53HomePositionX)); // retract + writeBlock(gFormat.format(53), gMotionModal.format(0), "Z" + zFormat.format(currentSection.spindle == SPINDLE_SECONDARY ? properties.g53HomePositionSubZ : properties.g53HomePositionZ)); // retract + + onCommand(COMMAND_UNLOCK_MULTI_AXIS); + if (!properties.optimizeCaxisSelect) { + cAxisEngageModal.reset(); + } + writeBlock( + getCode("FEED_MODE_UNIT_MIN"), + gPlaneModal.format(17), + getCode("DISENGAGE_C_AXIS") + ); + // setCoolant(COOLANT_THROUGH_TOOL); + gSpindleModeModal.reset(); + writeBlock( + getCode("CONSTANT_SURFACE_SPEED_OFF"), + sOutput.format(50), + getCode(currentSection.spindle == SPINDLE_SECONDARY ? "START_SUB_SPINDLE_CW" : "START_MAIN_SPINDLE_CW") + ); + // writeBlock(mFormat.format(getCode("INTERLOCK_BYPASS", getSpindle(true)))); + writeBlock(getCode("PART_CATCHER_ON")); + writeBlock(getCode(currentSection.spindle == SPINDLE_SECONDARY ? "UNCLAMP_SECONDARY_CHUCK" : "UNCLAMP_PRIMARY_CHUCK")); + onDwell(1.5); + // writeBlock(mFormat.format(getCode("CYCLE_PART_EJECTOR"))); + // onDwell(0.5); + writeBlock(getCode("PART_CATCHER_OFF")); + onDwell(1.1); + + // clean out chips + if (properties.airCleanChuck) { + writeBlock(getCode(currentSection.spindle == SPINDLE_SECONDARY ? "SUBSPINDLE_AIR_BLAST_ON" : "MAINSPINDLE_AIR_BLAST_ON"), + formatComment("AIR BLAST ON")); + onDwell(2.5); + writeBlock(getCode(currentSection.spindle == SPINDLE_SECONDARY ? "SUBSPINDLE_AIR_BLAST_OFF" : "MAINSPINDLE_AIR_BLAST_OFF"), + formatComment("AIR BLAST OFF")); + } + writeBlock(getCode("STOP_SPINDLE")); + setCoolant(COOLANT_OFF); + writeComment(localize("END OF PART EJECT")); + writeln(""); +} + +function onSectionEnd() { + + if (currentSection.partCatcher) { + engagePartCatcher(false); + } + + if (machineState.usePolarMode) { + setPolarMode(false); // disable polar interpolation mode + } + + // cancel SFM mode to preserve spindle speed + if ((tool.getSpindleMode() == SPINDLE_CONSTANT_SURFACE_SPEED) && !machineState.stockTransferIsActive) { + startSpindle(true, getFramePosition(currentSection.getFinalPosition())); + } + + if (properties.useG61) { + writeBlock(gExactStopModal.format(64)); + } + + if (((getCurrentSectionId() + 1) >= getNumberOfSections()) || + (tool.number != getNextSection().getTool().number)) { + onCommand(COMMAND_BREAK_CONTROL); + } + + if ((currentSection.getType() == TYPE_MILLING) && + (!hasNextSection() || (hasNextSection() && (getNextSection().getType() != TYPE_MILLING)))) { + // exit milling mode + if (isSameDirection(currentSection.workPlane.forward, new Vector(0, 0, 1))) { + // +Z + } else { + writeBlock(getCode("STOP_SPINDLE")); + } + } + + if (machineState.cAxisIsEngaged && !properties.optimizeCAxisSelect) { + writeBlock(getCode("DISENGAGE_C_AXIS")); // used for c-axis encoder reset + forceWorkPlane(); // needed since re-engage would result in undefined c-axis position + } + + forceAny(); + forcePolarMode = false; + partCutoff = false; +} + +function onClose() { + writeln(""); + + optionalSection = false; + + onCommand(COMMAND_COOLANT_OFF); + + if (properties.gotChipConveyor) { + onCommand(COMMAND_STOP_CHIP_TRANSPORT); + } + + if (getNumberOfSections() > 0) { // Retracting Z first causes safezone overtravel error to keep from crashing into subspindle. Z should already be retracted to and end of section. + var section = getSection(getNumberOfSections() - 1); + if ((section.getType() != TYPE_TURNING) && isSameDirection(section.workPlane.forward, new Vector(0, 0, 1))) { + writeBlock(gFormat.format(53), gMotionModal.format(0), "X" + xFormat.format(properties.g53HomePositionX), conditional(gotYAxis, "Y" + yFormat.format(properties.g53HomePositionY))); // retract + xOutput.reset(); + yOutput.reset(); + writeBlock(gFormat.format(53), gMotionModal.format(0), "Z" + zFormat.format((currentSection.spindle == SPINDLE_SECONDARY) ? properties.g53HomePositionSubZ : properties.g53HomePositionZ)); // retract + zOutput.reset(); + writeBlock(getCode("STOP_SPINDLE")); + } else { + if (gotYAxis) { + writeBlock(gFormat.format(53), gMotionModal.format(0), "Y" + yFormat.format(properties.g53HomePositionY)); // retract + } + writeBlock(gFormat.format(53), gMotionModal.format(0), "X" + xFormat.format(properties.g53HomePositionX)); // retract + xOutput.reset(); + yOutput.reset(); + writeBlock(gFormat.format(53), gMotionModal.format(0), "Z" + zFormat.format(currentSection.spindle == SPINDLE_SECONDARY ? properties.g53HomePositionSubZ : properties.g53HomePositionZ)); // retract + zOutput.reset(); + writeBlock(getCode("STOP_SPINDLE")); + } + } + if (machineState.tailstockIsActive) { + writeBlock(getCode("TAILSTOCK_OFF")); + } + + gMotionModal.reset(); + if (!properties.optimizeCAxisSelect) { + cAxisEngageModal.reset(); + } + writeBlock(getCode("DISENGAGE_C_AXIS")); + + if (ejectRoutine) { + ejectPart(); + } + + if (gotYAxis) { + writeBlock(gFormat.format(53), gMotionModal.format(0), "Y" + yFormat.format(properties.g53HomePositionY)); + yOutput.reset(); + } + + if (properties.useBarFeeder) { + writeln(""); + writeComment(localize("Bar feed")); + // feed bar here + // writeBlock(gFormat.format(53), gMotionModal.format(0), "X" + xFormat.format(properties.g53HomePositionX)); + writeBlock(gFormat.format(105)); + } + + writeln(""); + onImpliedCommand(COMMAND_END); + onImpliedCommand(COMMAND_STOP_SPINDLE); + if (properties.looping) { + writeBlock(mFormat.format(99)); + } else if (true /*!properties.useM97*/) { + writeBlock(mFormat.format(properties.useBarFeeder ? 99 : 30)); // stop program, spindle stop, coolant off + } else { + writeBlock(mFormat.format(99)); + } + writeln("%"); +} +// <<<<< INCLUDED FROM ../common/haas lathe.cps + +properties.maximumSpindleSpeed = 4000; diff --git a/Haas_Turning/haas turning.cps b/Haas_Turning/haas turning.cps new file mode 100644 index 0000000..139c301 --- /dev/null +++ b/Haas_Turning/haas turning.cps @@ -0,0 +1,1727 @@ +/** + Copyright (C) 2012-2019 by Autodesk, Inc. + All rights reserved. + + HAAS Lathe post processor configuration. + + $Revision: 42631 621a6aaa12feb9f27eb24e2b06cf85758a41d354 $ + $Date: 2020-01-21 14:14:08 $ + + FORKID {22A4780E-F937-4B1E-8446-D53DB2F57573} +*/ + +description = "HAAS Turning"; +vendor = "Haas Automation"; +vendorUrl = "https://www.haascnc.com"; +legal = "Copyright (C) 2012-2019 by Autodesk, Inc."; +certificationLevel = 2; +minimumRevision = 40783; + +longDescription = "Generic HAAS turning post. Turn on the property 'manualToolChange' if your CNC does not have an automatic tool changer. 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(355); +allowHelicalMoves = true; +allowedCircularPlanes = undefined; // allow any circular motion +allowSpiralMoves = true; +highFeedrate = (unit == IN) ? 100 : 5000; + +// user-defined properties +properties = { + writeMachine: false, // write machine + writeTools: false, // writes the tools + showSequenceNumbers: true, // show sequence numbers + sequenceNumberStart: 10, // first sequence number + sequenceNumberIncrement: 1, // increment for sequence numbers + optionalStop: true, // optional stop + separateWordsWithSpace: true, // specifies that the words should be separated with a white space + useRadius: false, // specifies that arcs should be output using the radius (R word) instead of the I, J, and K words. + maximumSpindleSpeed: 100 * 60, // specifies the maximum spindle speed + useParametricFeed: false, // specifies that feed should be output using Q values + showNotes: false, // specifies that operation notes should be output. + useCycles: true, // specifies that drilling cycles should be used. + manualToolChange: false, // specifies that the machine does not have a tool changer. + g53HomePositionX: 0, // home position for X-axis + g53HomePositionZ: 0, // home position for Z-axis + useTailStock: false, // specifies to use the tailstock or not + gotChipConveyor: true // specifies to use a chip conveyor Y/N +}; + +propertyDefinitions = { + writeMachine: {title:"Write machine", description:"Output the machine settings in the header of the code.", group:0, type:"boolean"}, + writeTools: {title:"Write tool list", description:"Output a tool list in the header of the code.", group:0, type:"boolean"}, + showSequenceNumbers: {title:"Use sequence numbers", description:"Use sequence numbers for each block of outputted code.", group:1, type:"boolean"}, + sequenceNumberStart: {title:"Start sequence number", description:"The number at which to start the sequence numbers.", group:1, type:"integer"}, + sequenceNumberIncrement: {title:"Sequence number increment", description:"The amount by which the sequence number is incremented by in each block.", group:1, type:"integer"}, + optionalStop: {title:"Optional stop", description:"Specifies that optional stops M1 should be output at tool changes.", type:"boolean"}, + separateWordsWithSpace: {title:"Separate words with space", description:"Adds spaces between words if 'yes' is selected.", type:"boolean"}, + useRadius: {title:"Radius arcs", description:"If yes is selected, arcs are output using radius values rather than IJK.", type:"boolean"}, + maximumSpindleSpeed: {title:"Maximum spindle speed", description:"The maximum spindle speed in RPM.", type:"integer"}, + useParametricFeed: {title:"Parametric feed", description:"Parametric feed values based on movement type are output.", type:"boolean"}, + showNotes: {title:"Show notes", description:"Enable to output notes for operations.", type:"boolean"}, + useCycles: {title:"Use canned cycles", description:"Enable to output canned cycles.", type:"boolean"}, + manualToolChange: {title:"Manual tool changer", description:"Enable to do manual tool changes when the CNC doesn't have an automatic tool changer.", type:"boolean"}, + g53HomePositionX: {title:"G53 home Position X", description:"G53 X-axis home position.", type:"number"}, + g53HomePositionZ: {title:"G53 home Position Z", description:"G53 Z-axis home position.", type:"number"}, + useTailStock: {title:"Use tail stock", description:"Enable to use the tail stock.", type:"boolean"}, + gotChipConveyor: {title:"Got chip conveyor", description:"Specifies whether to use a chip conveyor.", type:"boolean", presentation:"yesno"} +}; + +var permittedCommentChars = " ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789.,=_-"; + +var gFormat = createFormat({prefix:"G", decimals:0}); +var mFormat = createFormat({prefix:"M", decimals:0}); + +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 rFormat = createFormat({decimals:(unit == MM ? 3 : 4), forceDecimal:true}); // radius +var feedFormat = createFormat({decimals:(unit == MM ? 4 : 5), 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 milliFormat = createFormat({decimals:0}); // milliseconds // range 1-9999 +var taperFormat = createFormat({decimals:1, scale:DEG}); + +var xOutput = createVariable({prefix:"X"}, xFormat); +var yOutput = createVariable({prefix:"Y"}, yFormat); +var zOutput = createVariable({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); +var pOutput = createVariable({prefix:"P", force:true}, rpmFormat); + +// circular output +var iOutput = createReferenceVariable({prefix:"I", force:true}, spatialFormat); +var jOutput = createReferenceVariable({prefix:"J", force:true}, spatialFormat); +var kOutput = createReferenceVariable({prefix:"K", force:true}, spatialFormat); + +var g92IOutput = createVariable({prefix:"I"}, zFormat); // no scaling + +var gMotionModal = createModal({}, gFormat); // modal group 1 // G0-G3, ... +var gPlaneModal = createModal({onchange:function () {gMotionModal.reset();}}, gFormat); // modal group 2 // G17-19 +var gFeedModeModal = createModal({}, gFormat); // modal group 5 // G98-99 +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 = 100; +var gotSecondarySpindle = true; +var gotDoorControl = true; +var gotTailStock = true; +var gotBarFeeder = true; + +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 activeMovements; // do not use by default +var currentFeedId; +var maximumCircularRadiiDifference = toPreciseUnit(0.005, MM); +var toolingData; +var previousToolingData; + +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(21); + case "TAILSTOCK_OFF": + return mFormat.format(22); + // 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(143); + case "START_SUB_SPINDLE_CCW": + // machineState.subSpindleIsActive = true; + return mFormat.format(144); + // 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(99); + case "FEED_MODE_UNIT_MIN": + return gFeedModeModal.format(98); + 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(UNSUPPORTED); + // case "MAINSPINDLE_AIR_BLAST_OFF": + // return mFormat.format(UNSUPPORTED); + // 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(UNSUPPORTED); + // case "UNCLAMP_PRIMARY_CHUCK": + // return mFormat.format(UNSUPPORTED); + // 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(UNSUPPORTED); + // case "STOP_CHIP_TRANSPORT": + // return mFormat.format(UNSUPPORTED); + // case "OPEN_DOOR": + // return mFormat.format(UNSUPPORTED); + // case "CLOSE_DOOR": + // return mFormat.format(UNSUPPORTED); + case "COOLANT_FLOOD_ON": + return mFormat.format(8); + case "COOLANT_FLOOD_OFF": + return mFormat.format(9); + case "COOLANT_AIR_ON": + return mFormat.format(83); + case "COOLANT_AIR_OFF": + return mFormat.format(84); + case "COOLANT_THROUGH_TOOL_ON": + return mFormat.format(88); + case "COOLANT_THROUGH_TOOL_OFF": + return mFormat.format(89); + case "COOLANT_OFF": + return mFormat.format(9); + default: + error(localize("Command " + code + " is not defined.")); + return 0; + } +} + +/** + Writes the specified block. +*/ +function writeBlock() { + var text = formatWords(arguments); + if (!text) { + return; + } + if (properties.showSequenceNumbers) { + if (optionalSection) { + if (text) { + writeWords("/", "N" + sequenceNumber, text); + } + } else { + writeWords2("N" + sequenceNumber, arguments); + } + sequenceNumber += properties.sequenceNumberIncrement; + } else { + if (optionalSection) { + writeWords2("/", arguments); + } else { + writeWords(arguments); + } + } +} + +/** + Writes the specified optional block. +*/ +function writeOptionalBlock() { + if (properties.showSequenceNumbers) { + var words = formatWords(arguments); + if (words) { + writeWords("/", "N" + sequenceNumber, words); + sequenceNumber += properties.sequenceNumberIncrement; + } + } else { + writeWords2("/", arguments); + } +} + +function formatComment(text) { + return "(" + String(text).replace(/[()]/g, "") + ")"; +} + +/** + Output a comment. +*/ +function writeComment(text) { + writeln(formatComment(text)); +} + +function onOpen() { + if (properties.useRadius) { + maximumCircularSweep = toRad(90); // avoid potential center calculation errors for CNC + } + + yOutput.disable(); + + if (highFeedrate <= 0) { + error(localize("You must set 'highFeedrate' because axes are not synchronized for rapid traversal.")); + return; + } + + if (!properties.separateWordsWithSpace) { + setWordSeparator(""); + } + + sequenceNumber = properties.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 (properties.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; + } + } + } + + // dump tool information + if (properties.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(getCode("FEED_MODE_UNIT_MIN"), gPlaneModal.format(18)); + + switch (unit) { + case IN: + writeBlock(gUnitModal.format(20)); + break; + case MM: + writeBlock(gUnitModal.format(21)); + break; + } + + var usesPrimarySpindle = false; + var usesSecondarySpindle = false; + for (var i = 0; i < getNumberOfSections(); ++i) { + var section = getSection(i); + if (section.getType() != TYPE_TURNING) { + continue; + } + switch (section.spindle) { + case SPINDLE_PRIMARY: + usesPrimarySpindle = true; + break; + case SPINDLE_SECONDARY: + usesSecondarySpindle = true; + break; + } + } + if (usesPrimarySpindle) { + writeBlock(gFormat.format(50), sOutput.format(properties.maximumSpindleSpeed)); + sOutput.reset(); + } + if (gotSecondarySpindle) { + if (usesSecondarySpindle) { + writeBlock(gFormat.format(50), pOutput.format(properties.maximumSpindleSpeed)); + pOutput.reset(); + } + } + + 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 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 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; + 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)) { + var feedContext = new FeedContext(id, localize("High Feed"), this.highFeedrate); + activeFeeds.push(feedContext); + activeMovements[MOVEMENT_HIGH_FEED] = 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 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 retracted = false; // specifies that the tool has been retracted to the safe plane + var newSpindle = isFirstSection() || + (getPreviousSection().spindle != currentSection.spindle); + var newWorkOffset = isFirstSection() || + (getPreviousSection().workOffset != currentSection.workOffset); // work offset changes + var newWorkPlane = isFirstSection() || + !isSameDirection(getPreviousSection().getGlobalFinalToolAxis(), currentSection.getGlobalInitialToolAxis()); + + // 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; + } + + // turning using front tool post + if (toolingData.toolPost == FRONT) { + xFormat = createFormat({decimals:(unit == MM ? 3 : 4), forceDecimal:true, scale:-2}); + xOutput = createVariable({prefix:"X"}, xFormat); + iFormat = createFormat({decimals:(unit == MM ? 3 : 4), forceDecimal:true, scale:-1}); // radius mode + iOutput = createReferenceVariable({prefix:"I"}, iFormat); + + // turning using rear tool post + } else { + xFormat = createFormat({decimals:(unit == MM ? 3 : 4), forceDecimal:true, scale:2}); + xOutput = createVariable({prefix:"X"}, xFormat); + iFormat = createFormat({decimals:(unit == MM ? 3 : 4), forceDecimal:true, scale:1}); // radius mode + iOutput = createReferenceVariable({prefix:"I"}, iFormat); + } + + if (insertToolCall || newSpindle || newWorkOffset || newWorkPlane) { + + // retract to safe plane + retracted = true; + if (!isFirstSection() && insertToolCall) { + onCommand(COMMAND_COOLANT_OFF); + } + //writeBlock(gFormat.format(53), gMotionModal.format(0), "Z" + zFormat.format(properties.g53HomePositionZ)); // retract + //zOutput.reset(); + writeBlock(gFormat.format(28)); // retract + xOutput.reset(); + } + + writeln(""); + + if (hasParameter("operation-comment")) { + var comment = getParameter("operation-comment"); + if (comment) { + writeComment(comment); + } + } + + if (properties.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) { + retracted = true; + gFeedModeModal.reset(); + + if (!isFirstSection() && properties.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; + } + writeBlock("T" + toolFormat.format(tool.number * 100 + compensationOffset)); + if (tool.comment) { + writeComment(tool.comment); + } + if (tool.manualToolChange || properties.manualToolChange) { + onCommand(COMMAND_STOP); + writeComment("MANUAL TOOL CHANGE TO T" + toolFormat.format(tool.number * 100 + compensationOffset)); + } + } + + if (newSpindle) { + // select spindle if required + } + + // see page 138 in 96-8700an for stock transfer / G199/G198 + if (currentSection.feedMode == FEED_PER_REVOLUTION) { + writeBlock(getCode("FEED_MODE_UNIT_REV")); + } else { + writeBlock(getCode("FEED_MODE_UNIT_MIN")); + } + + if (insertToolCall || + newSpindle || + isFirstSection() || + (isSpindleSpeedDifferent())) { + + if (!turning) { + if (spindleSpeed < 1) { + error(localize("Spindle speed out of range.")); + return; + } + if (spindleSpeed > 3000) { + warning(localize("Spindle speed exceeds maximum value.")); + } + } + + switch (currentSection.spindle) { + case SPINDLE_PRIMARY: // main spindle + if (properties.useTailStock) { + if (gotTailStock) { + writeBlock(getCode(currentSection.tailstock ? "TAILSTOCK_ON" : "TAILSTOCK_OFF")); + } + // writeBlock(mFormat.format(clampPrimaryChuck ? 10 : 11)); + // writeBlock(mFormat.format(clampSecondaryChuck ? 110 : 111)); + } + break; + case SPINDLE_SECONDARY: // sub spindle + if (!gotSecondarySpindle) { + error(localize("Secondary spindle is not available.")); + return; + } + if (properties.useTailStock) { + // use could also swap spindles using G14/G15 + if (gotTailStock && currentSection.tailstock) { + error(localize("Tail stock is not supported for secondary spindle.")); + return; + // writeBlock(getCode(currentSection.tailstock ? "TAILSTOCK_ON" : "TAILSTOCK_OFF")); + } + // writeBlock(mFormat.format(clampPrimaryChuck ? 10 : 11)); + // writeBlock(mFormat.format(clampSecondaryChuck ? 110 : 111)); + } + break; + } + startSpindle(false, true, getFramePosition(currentSection.getInitialPosition())); + } + + // 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 (workOffset > 6) { + var code = workOffset - 6; + if (code > 99) { + error(localize("Work offset out of range.")); + return; + } + if (workOffset != currentWorkOffset) { + writeBlock(gFormat.format(154), "P" + code); + currentWorkOffset = workOffset; + } + } else { + if (workOffset != currentWorkOffset) { + writeBlock(gFormat.format(53 + workOffset)); // G54->G59 + currentWorkOffset = workOffset; + } + } + } + + // set coolant after we have positioned at Z + setCoolant(tool.coolant); + + forceAny(); + gMotionModal.reset(); + + setRotation(currentSection.workPlane); + + var initialPosition = getFramePosition(currentSection.getInitialPosition()); + if (!retracted) { + // TAG: need to retract along X or Z + if (getCurrentPosition().z < initialPosition.z) { + writeBlock(gMotionModal.format(0), zOutput.format(initialPosition.z)); + } + } + + if (insertToolCall || tool.getSpindleMode() == SPINDLE_CONSTANT_SURFACE_SPEED) { + gMotionModal.reset(); + 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 (currentSection.partCatcher) { + engagePartCatcher(true); + } + + if (properties.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) { + gPlaneModal.reset(); + } +} + +function onDwell(seconds) { + if (seconds > 99999.999) { + warning(localize("Dwelling time is out of range.")); + } + milliseconds = clamp(1, seconds * 1000, 99999999); + writeBlock(/*gFeedModeModal.format(94),*/ gFormat.format(4), "P" + milliFormat.format(milliseconds)); +} + +var pendingRadiusCompensation = -1; + +function onRadiusCompensation() { + pendingRadiusCompensation = radiusCompensation; +} + +var resetFeed = false; + +function getHighfeedrate(radius) { + if (currentSection.feedMode == FEED_PER_REVOLUTION) { + var rpm = spindleSpeed; // rev/min + if (currentSection.getTool().getSpindleMode() == SPINDLE_CONSTANT_SURFACE_SPEED) { + var O = 2 * Math.PI * radius; // in/rev + rpm = tool.surfaceSpeed / O; // in/min div in/rev => rev/min + } + return highFeedrate / rpm; // in/min div rev/min => in/rev + } + return highFeedrate; +} + +function onRapid(_x, _y, _z) { + var x = xOutput.format(_x); + var y = yOutput.format(_y); + var z = zOutput.format(_z); + if (x || y || z) { + var useG1 = ((x ? 1 : 0) + (y ? 1 : 0) + (z ? 1 : 0)) > 1 && !isCannedCycle; + if (pendingRadiusCompensation >= 0) { + pendingRadiusCompensation = -1; + if (useG1) { + switch (radiusCompensation) { + case RADIUS_COMPENSATION_LEFT: + writeBlock(gMotionModal.format(1), gFormat.format(41), x, y, z, getFeed(getHighfeedrate(_x))); + break; + case RADIUS_COMPENSATION_RIGHT: + writeBlock(gMotionModal.format(1), gFormat.format(42), x, y, z, getFeed(getHighfeedrate(_x))); + break; + default: + writeBlock(gMotionModal.format(1), gFormat.format(40), x, y, z, getFeed(getHighfeedrate(_x))); + } + } else { + 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); + } + } + } + if (useG1) { + // axes are not synchronized + writeBlock(gMotionModal.format(1), x, y, z, getFeed(getHighfeedrate(_x))); + } else { + writeBlock(gMotionModal.format(0), x, y, z); + forceFeed(); + } + resetFeed = false; + } +} + +function onLinear(_x, _y, _z, feed) { + if (isSpeedFeedSynchronizationActive()) { + resetFeed = true; + var threadPitch = getParameter("operation:threadPitch"); + var threadsPerInch = 1.0 / threadPitch; // per mm for metric + writeBlock(gMotionModal.format(32), xOutput.format(_x), yOutput.format(_y), zOutput.format(_z), pitchOutput.format(1 / threadsPerInch)); + 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; + writeBlock(gPlaneModal.format(18)); + 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 (properties.useRadius || isHelical()) { // radius mode does not support full arcs + linearize(tolerance); + return; + } + switch (getCircularPlane()) { + case PLANE_XY: + writeBlock(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(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(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 (!properties.useRadius) { + switch (getCircularPlane()) { + case PLANE_XY: + writeBlock(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(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(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(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(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(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 = true; +var xyzFormat = createFormat({decimals:(unit == MM ? 4 : 5), forceDecimal:true}); +var pathBlockNumber = {start: 0, end: 0}; +var isCannedCycle = false; + +function onCyclePath() { + saveShowSequenceNumbers = properties.showSequenceNumbers; + isCannedCycle = true; + // buffer all paths and stop feeds being output + feedOutput.disable(); + properties.showSequenceNumbers = false; + redirectToBuffer(); + gMotionModal.reset(); + if ((hasParameter("operation:grooving") && getParameter("operation:grooving").toUpperCase() != "OFF")) { + forceXYZ(); + } +} + +function onCyclePathEnd() { + properties.showSequenceNumbers = saveShowSequenceNumbers; // reset property to initial state + feedOutput.enable(); + var cyclePath = String(getRedirectionBuffer()).split(EOL); // get cycle path from buffer + closeRedirection(); + for (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), + "P" + (getStartEndSequenceNumber(cyclePath, true)), + "Q" + (getStartEndSequenceNumber(cyclePath, false)), + "U" + xFormat.format(cycle.xStockToLeave), + "W" + xyzFormat.format(cycle.zStockToLeave), + "D" + xyzFormat.format(cycle.depthOfCut), + 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 + properties.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 + properties.showSequenceNumbers = saveShowSequenceNumbers; // reset property to initial state + isCannedCycle = false; + } +} + +function getStartEndSequenceNumber(cyclePath, start) { + if (start) { + pathBlockNumber.start = sequenceNumber + conditional(saveShowSequenceNumbers, properties.sequenceNumberIncrement); + return pathBlockNumber.start; + } else { + pathBlockNumber.end = sequenceNumber + properties.sequenceNumberIncrement + conditional(saveShowSequenceNumbers, (cyclePath.length - 1) * properties.sequenceNumberIncrement); + return pathBlockNumber.end; + } +} + +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" + spatialFormat.format(r)]; +} + +function onCyclePoint(x, y, z) { + if (!properties.useCycles || currentSection.isMultiAxis()) { + expandCyclePoint(x, y, z); + return; + } + + if (isSameDirection(currentSection.workPlane.forward, new Vector(0, 0, 1)) || + isSameDirection(currentSection.workPlane.forward, new Vector(0, 0, -1))) { + gPlaneModal.format(17); // 2-axis lathes typically don't use G17 + } else { + expandCyclePoint(x, y, z); + return; + } + + switch (cycleType) { + case "thread-turning": + var inverted = (toolingData.toolPost == REAR) ? 1 : -1; + var i = -cycle.incrementalX * inverted; // positive if taper goes down - delta radius + var threadsPerInch = 1.0 / cycle.pitch; // per mm for metric + var f = 1 / threadsPerInch; + // writeBlock(mFormat.format(23)); // chamfer/fade on - fading is default behavior + writeBlock(gMotionModal.format(92), xOutput.format(x - cycle.incrementalX), yOutput.format(y), zOutput.format(z), conditional(zFormat.isSignificant(i), g92IOutput.format(i)), pitchOutput.format(f)); + // writeBlock(mFormat.format(24)); // chamfer/fade off + forceFeed(); + return; + } + + if (isFirstCyclePoint()) { + repositionToCycleClearance(cycle, x, y, z); + + var F = cycle.feedrate; + var P = !cycle.dwell ? 0 : clamp(1, cycle.dwell * 1000, 99999999); // in milliseconds + + 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), + "P" + milliFormat.format(P), + feedOutput.format(F) + ); + } else { + writeBlock( + gCycleModal.format(81), + getCommonCycle(x, y, z, cycle.retract), + feedOutput.format(F) + ); + } + break; + case "chip-breaking": + case "deep-drilling": + // either use Q or I,J,K + writeBlock( + gCycleModal.format(83), + getCommonCycle(x, y, z, cycle.retract), + "Q" + spatialFormat.format(cycle.incrementalDepth), + // conditional(cycle.incrementalDepthReduction > 0, "J" + spatialFormat.format(cycle.incrementalDepthReduction)), + // conditional(cycle.minimumIncrementalDepth > 0, "K" + spatialFormat.format(cycle.minimumIncrementalDepth)), + conditional(P > 0, "P" + milliFormat.format(P)), + feedOutput.format(F) + ); + break; + case "tapping": + F = tool.getThreadPitch() * rpmFormat.getResultingValue(spindleSpeed); + writeBlock( + gCycleModal.format((tool.type == TOOL_TAP_LEFT_HAND) ? 184 : 84), + getCommonCycle(x, y, z, cycle.retract), + "P" + milliFormat.format(P), + pitchOutput.format(F) + ); + forceFeed(); + break; + case "left-tapping": + F = tool.getThreadPitch() * rpmFormat.getResultingValue(spindleSpeed); + writeBlock( + gCycleModal.format(184), + getCommonCycle(x, y, z, cycle.retract), + "P" + milliFormat.format(P), + pitchOutput.format(F) + ); + forceFeed(); + break; + case "right-tapping": + F = tool.getThreadPitch() * rpmFormat.getResultingValue(spindleSpeed); + writeBlock( + gCycleModal.format(84), + getCommonCycle(x, y, z, cycle.retract), + "P" + milliFormat.format(P), + pitchOutput.format(F) + ); + forceFeed(); + break; + case "tapping-with-chip-breaking": + case "left-tapping-with-chip-breaking": + case "right-tapping-with-chip-breaking": + F = tool.getThreadPitch() * rpmFormat.getResultingValue(spindleSpeed); + writeBlock( + gCycleModal.format((tool.type == TOOL_TAP_LEFT_HAND ? 184 : 84)), + getCommonCycle(x, y, z, cycle.retract), + "P" + milliFormat.format(P), + "Q" + spatialFormat.format(cycle.incrementalDepth), + pitchOutput.format(F) + ); + forceFeed(); + break; + case "fine-boring": + writeBlock( + gCycleModal.format(76), + getCommonCycle(x, y, z, cycle.retract), + "P" + milliFormat.format(P), // not optional + "Q" + spatialFormat.format(cycle.shift), + feedOutput.format(F) + ); + break; + case "reaming": + if (P > 0) { + writeBlock( + gCycleModal.format(89), + getCommonCycle(x, y, z, cycle.retract), + "P" + milliFormat.format(P), + feedOutput.format(F) + ); + } else { + writeBlock( + gCycleModal.format(85), + getCommonCycle(x, y, z, cycle.retract), + feedOutput.format(F) + ); + } + break; + case "stop-boring": + if (P > 0) { + expandCyclePoint(x, y, z); + } else { + writeBlock( + gCycleModal.format(86), + getCommonCycle(x, y, z, cycle.retract), + feedOutput.format(F) + ); + } + break; + case "boring": + if (P > 0) { + writeBlock( + gCycleModal.format(89), + getCommonCycle(x, y, z, cycle.retract), + "P" + milliFormat.format(P), // not optional + feedOutput.format(F) + ); + } else { + writeBlock( + 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) { + switch (cycleType) { + case "thread-turning": + forceFeed(); + xOutput.reset(); + zOutput.reset(); + g92IOutput.reset(); + break; + default: + writeBlock(gCycleModal.format(80)); + } + } +} + +var currentCoolantMode = COOLANT_OFF; + +function setCoolant(coolant) { + if (coolant == currentCoolantMode) { + return; // coolant is already active + } + + var m = undefined; + if (coolant == COOLANT_OFF) { + if (currentCoolantMode == COOLANT_THROUGH_TOOL) { + writeBlock(getCode("COOLANT_THROUGH_TOOL_OFF")); + } else if (currentCoolantMode == COOLANT_AIR) { + writeBlock(getCode("COOLANT_AIR_OFF")); + } else { + writeBlock(getCode("COOLANT_OFF")); + } + currentCoolantMode = COOLANT_OFF; + return; + } + + switch (coolant) { + case COOLANT_FLOOD: + m = getCode("COOLANT_FLOOD_ON"); + break; + case COOLANT_THROUGH_TOOL: + m = getCode("COOLANT_THROUGH_TOOL_ON"); + break; + case COOLANT_AIR: + m = getCode("COOLANT_AIR_ON"); + break; + default: + onUnsupportedCoolant(coolant); + m = getCode("COOLANT_OFF"); + } + + if (m) { + writeBlock(m); + currentCoolantMode = coolant; + } +} + +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; +} + +function onSpindleSpeed(spindleSpeed) { + if ((sOutput.getCurrent() != Number.POSITIVE_INFINITY) && rpmFormat.areDifferent(spindleSpeed, sOutput.getCurrent())) { // avoid redundant output of spindle speed + startSpindle(false, false, getFramePosition(currentSection.getInitialPosition()), spindleSpeed); + } + if ((pOutput.getCurrent() != Number.POSITIVE_INFINITY) && rpmFormat.areDifferent(spindleSpeed, pOutput.getCurrent())) { // avoid redundant output of spindle speed + startSpindle(false, false, getFramePosition(currentSection.getInitialPosition()), spindleSpeed); + } +} + +function startSpindle(tappingMode, forceRPMMode, initialPosition, rpm) { + var spindleDir; + var spindleMode; + var _spindleSpeed = spindleSpeed; + if (rpm !== undefined) { + _spindleSpeed = rpm; + } + + gSpindleModeModal.reset(); + + if ((getSpindle() == SPINDLE_SECONDARY) && !gotSecondarySpindle) { + error(localize("Secondary spindle is not available.")); + return; + } + + if (false /*tappingMode*/) { + spindleDir = getCode("RIGID_TAPPING"); + } else { + 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, properties.maximumSpindleSpeed) : properties.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(50), sOutput.format(maximumSpindleSpeed)); + spindleMode = getCode("CONSTANT_SURFACE_SPEED_ON"); + } + } else { + spindleMode = getCode("CONSTANT_SURFACE_SPEED_OFF"); + } + if (getSpindle(true) == SPINDLE_SECONDARY) { + writeBlock( + spindleMode, + pOutput.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: + writeBlock(mFormat.format((getSpindle() == 0) ? 14 : 114)); + break; + case COMMAND_UNLOCK_MULTI_AXIS: + writeBlock(mFormat.format((getSpindle() == 0) ? 15 : 115)); + break; + case COMMAND_START_CHIP_TRANSPORT: + if (properties.gotChipConveyor) { + writeBlock(mFormat.format(31)); + } + break; + case COMMAND_STOP_CHIP_TRANSPORT: + if (properties.gotChipConveyor) { + writeBlock(mFormat.format(33)); + } + break; + case COMMAND_OPEN_DOOR: + if (gotDoorControl) { + writeBlock(mFormat.format(85)); // optional + } + break; + case COMMAND_CLOSE_DOOR: + if (gotDoorControl) { + writeBlock(mFormat.format(86)); // optional + } + 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; + break; + case COMMAND_OPTIONAL_STOP: + writeBlock(mFormat.format(1)); + 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); + return; + 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: // add support for clamping + //case COMMAND_UNCLAMP: // 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); + writeBlock(gFormat.format(53), gMotionModal.format(0), "X" + xFormat.format(properties.g53HomePositionX)); // retract + writeBlock(gFormat.format(53), gMotionModal.format(0), "Z" + zFormat.format(properties.g53HomePositionZ)); // retract + 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(); +} + +function onClose() { + writeln(""); + + optionalSection = false; + + onCommand(COMMAND_COOLANT_OFF); + + if (properties.gotChipConveyor) { + onCommand(COMMAND_STOP_CHIP_TRANSPORT); + } + + // we might want to retract in Z before X + writeBlock(gFormat.format(53), gMotionModal.format(0), "X" + xFormat.format(properties.g53HomePositionX)); // retract + xOutput.reset(); + writeBlock(gFormat.format(53), gMotionModal.format(0), "Z" + zFormat.format(properties.g53HomePositionZ)); // retract + zOutput.reset(); + + onImpliedCommand(COMMAND_END); + onImpliedCommand(COMMAND_STOP_SPINDLE); + writeBlock(mFormat.format(30)); // stop program, spindle stop, coolant off + writeln("%"); +} diff --git a/README.md b/README.md index ec5996f..de080b9 100644 --- a/README.md +++ b/README.md @@ -1 +1,3 @@ -# PostUpdateProject \ No newline at end of file +# PostUpdateProject +Initial Commit 06/09/21 +Posts are consistent with those in PostProcessors Repo \ No newline at end of file