/** Copyright (C) 2012-2025 by Autodesk, Inc. All rights reserved. ShopBot OpenSBP post processor configuration. $Revision: 44164 505dc667441a42e0f0cf6120fba472bb8764dcef $ $Date: 2025-02-10 10:32:42 $ FORKID {866F31A2-119D-485c-B228-090CC89C9BE8} */ description = "ShopBot OpenSBP"; vendor = "ShopBot Tools"; vendorUrl = "http://www.shopbottools.com"; legal = "Copyright (C) 2012-2025 by Autodesk, Inc."; certificationLevel = 2; minimumRevision = 45702; longDescription = "Generic post for the Shopbot OpenSBP format with support for both manual and automatic tool changes. By default the post operates in 3-axis mode. For a 5-axis tool set the 'Five Axis' property to Yes. 5-axis users must set the 'Gauge length (IN)' property in inches before cutting which can be calculated through the tool's calibration macro. For a 4-axis tool set the 'Four Axis' property to YES. For 4-axis mode, the B-axis will turn around the X-axis by default. For the Y-axis configurations set the 'B axis rotates around X' property to NO. Users running older versions of SB3 - V3.5 or earlier should set the 'SB3 V3.6 or greater' property to NO."; extension = "sbp"; setCodePage("ascii"); capabilities = CAPABILITY_MILLING; tolerance = spatial(0.002, MM); minimumChordLength = spatial(0.25, MM); minimumCircularRadius = spatial(0.01, MM); maximumCircularRadius = spatial(1000, MM); minimumCircularSweep = toRad(0.01); maximumCircularSweep = toRad(180); allowHelicalMoves = true; allowedCircularPlanes = undefined; // allow any circular motion var maxZFeed = toPreciseUnit(180, IN); // max Z feed used for VS command var stockHeight; // user-defined properties properties = { fifthAxis: { title : "Five axis", description: "Defines whether the machine is a 5-axis model.", group : "configuration", type : "boolean", value : false, scope : "post" }, fourthAxis: { title : "Four axis", description: "Defines whether the machine is a 4-axis model.", group : "configuration", type : "boolean", value : false, scope : "post" }, bAxisTurnsAroundX: { title : "B axis rotates around X", description: "Choose between B-axis along X or Y. This is only applicable when the machine is a 4-axis model.", group : "configuration", type : "boolean", value : true, scope : "post" }, SB3v36: { title : "SB3 V3.6 or greater", description: "Specifies that the version of control is SB3 V3.6 or greater", group : "configuration", type : "boolean", value : true, scope : "post" }, gaugeLength: { title : "Gauge length (IN)", description: "Always set in inches. Change this for your particular machine and if recalibration is required. Use calibration macro to get value.", group : "multiAxis", type : "number", value : 6.3, scope : "post" }, safeRetractDistance: { title : "Safe retract distance", description: "A set distance to add to the tool length for rewind C-axis tool retract.", group : "multiAxis", type : "number", value : 2, scope : "post" }, useDPMFeeds: { title : "Rotary moves feed rate output", description: "'VS feeds' outputs DPM .", group : "multiAxis", type : "enum", values : [ {title:"VS feeds", id:"true"}, {title:"Linear axis MS feeds", id:"false"}, {title:"Programmed feeds", id:"tooltip"} ], value: "tooltip", scope: "post" }, minimizeFeedOutput: { title : "Minimize feedrate output", description: "Enable to limit feed rate output to the cutting (XY) and plunge (Z) feedrates (for multi-axis moves, 'Rotary moves feed rate output' must be set to 'Programmed feeds'). Disable to output all programmed feed rates.", group : "preferences", type : "boolean", value : true, scope : "post" } }; function CustomVariable(specifiers, format) { if (!(this instanceof CustomVariable)) { throw new Error(localize("CustomVariable constructor called as a function.")); } this.variable = createVariable(specifiers, format); this.offset = 0; } CustomVariable.prototype.format = function (value) { return this.variable.format(value + this.offset); }; CustomVariable.prototype.format2 = function (value) { return this.variable.format(value); }; CustomVariable.prototype.reset = function () { return this.variable.reset(); }; CustomVariable.prototype.disable = function () { return this.variable.disable(); }; CustomVariable.prototype.enable = function () { return this.variable.enable(); }; var xyzFormat = createFormat({decimals:(unit == MM ? 3 : 4)}); var abcFormat = createFormat({decimals:3, scale:DEG}); var feedFormat = createFormat({decimals:(unit == MM ? 3 : 4), scale:1.0 / 60.0}); // feed is mm/s or in/s var dpmFormat = createFormat({decimals:3, scale:1.0 / 60.0}); // feed is mm/s or in/s var secFormat = createFormat({decimals:2}); // seconds var rpmFormat = createFormat({decimals:0}); var xOutput = new CustomVariable({force:true}, xyzFormat); var yOutput = new CustomVariable({force:true}, xyzFormat); var zOutput = new CustomVariable({force:true}, xyzFormat); var aOutput = createVariable({force:true}, abcFormat); var bOutput = createVariable({force:true}, abcFormat); var feedOutput = createVariable({}, feedFormat); var dpmOutput1 = createVariable({}, dpmFormat); var dpmOutput2 = createVariable({}, dpmFormat); var feedZOutput = createVariable({force:true}, feedFormat); var sOutput = createVariable({prefix:"TR, ", force:true}, rpmFormat); // collected state var useSimpleFeeds; /** Writes the specified block. */ function writeBlock() { var result = ""; for (var i = 0; i < arguments.length; ++i) { if (i > 0) { result += ", "; } result += arguments[i]; } writeln(result); } /** Output a comment. */ function writeComment(text) { writeln("' " + text); } function onOpen() { if (getProperty("fifthAxis") && getProperty("fourthAxis")) { error(localize("You cannot enable both 'Five Axis' and 'Four Axis' properties at the same time.")); return; } if (getProperty("fifthAxis")) { var aAxis = createAxis({coordinate:0, table:false, axis:[0, 0, -1], range:[-450, 450], preference:0}); var bAxis = createAxis({coordinate:1, table:false, axis:[0, -1, 0], range:[-120, 120], preference:0}); machineConfiguration = new MachineConfiguration(bAxis, aAxis); setMachineConfiguration(machineConfiguration); optimizeMachineAngles2(0); // TCP mode - we compensate below } else if (getProperty("fourthAxis")) { if (getProperty("bAxisTurnsAroundX")) { // yes - still called B even when rotating around X-axis var bAxis = createAxis({coordinate:1, table:true, axis:[-1, 0, 0], cyclic:true, preference:1}); machineConfiguration = new MachineConfiguration(bAxis); setMachineConfiguration(machineConfiguration); optimizeMachineAngles2(1); } else { var bAxis = createAxis({coordinate:1, table:true, axis:[0, -1, 0], cyclic:true, preference:1}); machineConfiguration = new MachineConfiguration(bAxis); setMachineConfiguration(machineConfiguration); optimizeMachineAngles2(1); } } if (!machineConfiguration.isMachineCoordinate(0)) { aOutput.disable(); } if (!machineConfiguration.isMachineCoordinate(1)) { bOutput.disable(); } if (programName) { writeComment(programName); } if (programComment) { writeComment(programComment); } writeBlock("SA"); // absolute if (getProperty("SB3v36")) { writeln("CN, 90"); // calls up user variables in controller } switch (unit) { case IN: writeBlock("IF %(25)=1 THEN GOTO UNIT_ERROR"); break; case MM: writeBlock("IF %(25)=0 THEN GOTO UNIT_ERROR"); break; } var tools = getToolTable(); if ((tools.getNumberOfTools() > 1) && !getProperty("SB3v36")) { error(localize("Cannot use more than one tool without tool changer.")); return; } var workpiece = getWorkpiece(); stockHeight = workpiece.upper.z; writeBlock("&PWMaterial = " + xyzFormat.format(workpiece.upper.z - workpiece.lower.z)); var partDatum = workpiece.lower.z; if (partDatum >= 0) { writeBlock("&PWZorigin = Table Surface"); } else { writeBlock("&PWZorigin = Part Surface"); } machineConfiguration.setRetractPlane(stockHeight + getProperty("safeRetractDistance")); } 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(); } /** Force output of X, Y, Z, A, B, C, and F on next output. */ function forceAny() { forceXYZ(); forceABC(); previousDPMFeed[0] = 0; previousDPMFeed[1] = 0; feedOutput.reset(); } function onParameter(name, value) { } var currentWorkPlaneABC = undefined; function forceWorkPlane() { currentWorkPlaneABC = undefined; } function setWorkPlane(abc) { if (!machineConfiguration.isMultiAxisConfiguration()) { return true; // ignore } if (!((currentWorkPlaneABC == undefined) || abcFormat.areDifferent(abc.x, currentWorkPlaneABC.x) || abcFormat.areDifferent(abc.y, currentWorkPlaneABC.y) || abcFormat.areDifferent(abc.z, currentWorkPlaneABC.z))) { return false; // no change } // retract to safe plane writeBlock( "JZ", zOutput.format(machineConfiguration.getRetractPlane()) ); // move XY to home position writeBlock("JH"); writeBlock( "J5", "", // x "", // y "", // z conditional(machineConfiguration.isMachineCoordinate(0), abcFormat.format(abc.x)), conditional(machineConfiguration.isMachineCoordinate(1), abcFormat.format(abc.y)) // conditional(machineConfiguration.isMachineCoordinate(2), abcFormat.format(abc.z)) ); currentWorkPlaneABC = abc; return true; } var closestABC = false; // choose closest machine angles var currentMachineABC; function getWorkPlaneMachineABC(workPlane) { 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); 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 = getProperty("fifthAxis"); // 4-axis adjusts for rotations, 5-axis does not if (tcp) { setRotation(W); // TCP mode } else { var O = machineConfiguration.getOrientation(abc); var R = machineConfiguration.getRemainingOrientation(abc, W); setRotation(R); } return abc; } var headOffset = 0; function onSection() { var insertToolCall = isFirstSection() || currentSection.getForceToolChange && currentSection.getForceToolChange() || (tool.number != getPreviousSection().getTool().number); writeln(""); if (hasParameter("operation-comment")) { var comment = getParameter("operation-comment"); if (comment) { writeComment(comment); } } if (getProperty("showNotes") && hasParameter("notes")) { var notes = getParameter("notes"); if (notes) { var lines = String(notes).split("\n"); var r1 = new RegExp("^[\\s]+", "g"); var r2 = new RegExp("[\\s]+$", "g"); for (line in lines) { var comment = lines[line].replace(r1, "").replace(r2, ""); if (comment) { writeComment(comment); } } } } var retracted = false; if (machineConfiguration.isMultiAxisConfiguration()) { // use 5-axis indexing for multi-axis mode // set working plane after datum shift var abc; if (currentSection.isMultiAxis()) { abc = currentSection.getInitialToolAxisABC(); cancelTransformation(); } else { abc = getWorkPlaneMachineABC(currentSection.workPlane); } retracted = setWorkPlane(abc); } else { // pure 3D var remaining = currentSection.workPlane; if (!isSameDirection(remaining.forward, new Vector(0, 0, 1))) { error(localize("Tool orientation is not supported.")); return; } setRotation(remaining); } feedOutput.reset(); if (insertToolCall && getProperty("SB3v36")) { // forceWorkPlane(); if (tool.number > 99) { warning(localize("Tool number exceeds maximum value.")); } if (isFirstSection() || currentSection.getForceToolChange && currentSection.getForceToolChange() || (tool.number != getPreviousSection().getTool().number)) { /* if (hasParameter("operation:clearanceHeight_offset")) { var safeZ = getParameter("operation:clearanceHeight_offset"); writeln("&PWSafeZ = " + safeZ); } */ onCommand(COMMAND_STOP_SPINDLE); writeln("&Tool = " + tool.number); if (!currentSection.isMultiAxis() && !retracted) { writeln("C9"); // call macro 9 } } if (tool.comment) { writeln("&ToolName = \"" + tool.comment + "\""); } } /* if (!getProperty("SB3v36")) { // we only allow a single tool without a tool changer writeBlock("PAUSE"); // wait for user } */ if (insertToolCall || isFirstSection() || (rpmFormat.areDifferent(spindleSpeed, sOutput.getCurrent())) || (tool.clockwise != getPreviousSection().getTool().clockwise)) { if (spindleSpeed < 5000) { warning(localize("Spindle speed is below minimum value.")); } if (spindleSpeed > 24000) { warning(localize("Spindle speed exceeds maximum value.")); } writeBlock(sOutput.format(spindleSpeed)); onCommand(COMMAND_START_SPINDLE); } headOffset = 0; if (getProperty("fifthAxis")) { headOffset = tool.bodyLength + toPreciseUnit(getProperty("gaugeLength"), IN); // control will compensate for tool length var displacement = currentSection.getGlobalInitialToolAxis(); // var displacement = currentSection.workPlane.forward; displacement.multiply(headOffset); displacement = Vector.diff(displacement, new Vector(0, 0, headOffset)); // writeComment("DISPLACEMENT: X" + xyzFormat.format(displacement.x) + " Y" + xyzFormat.format(displacement.y) + " Z" + xyzFormat.format(displacement.z)); // setTranslation(displacement); // temporary solution xOutput.offset = displacement.x; yOutput.offset = displacement.y; zOutput.offset = displacement.z; } else { // temporary solution xOutput.offset = 0; yOutput.offset = 0; zOutput.offset = 0; } forceAny(); var initialPosition = getFramePosition(currentSection.getInitialPosition()); if (!retracted) { if (getCurrentPosition().z < initialPosition.z) { writeBlock("JZ", zOutput.format(initialPosition.z)); retracted = true; } else { retracted = false; } } if (true /*insertToolCall*/) { if (!retracted) { writeBlock( "JZ", zOutput.format(initialPosition.z) ); } writeBlock( "J2", xOutput.format(initialPosition.x), yOutput.format(initialPosition.y) ); } if (currentSection.isMultiAxis()) { xOutput.offset = 0; yOutput.offset = 0; zOutput.offset = 0; } useSimpleFeeds = false; if ((!currentSection.isMultiAxis() && getProperty("minimizeFeedOutput")) || (getProperty("minimizeFeedOutput") && getProperty("useDPMFeeds") == "tooltip")) { if (hasParameter("operation:tool_feedCutting") && hasParameter("operation:tool_feedPlunge")) { useSimpleFeeds = true; var f = feedOutput.format(getParameter("operation:tool_feedCutting")); var z = feedZOutput.format(Math.min(getParameter("operation:tool_feedPlunge"), maxZFeed)); if (f || z) { writeBlock("MS", f, z); } } } } function onDwell(seconds) { seconds = clamp(0.01, seconds, 99999); writeBlock("PAUSE", secFormat.format(seconds)); } function onSpindleSpeed(spindleSpeed) { if (spindleSpeed < 5000) { warning(localize("Spindle speed out of range.")); return; } if (spindleSpeed > 24000) { warning(localize("Spindle speed exceeds maximum value.")); } writeBlock(sOutput.format(spindleSpeed)); onCommand(COMMAND_START_SPINDLE); } function onRadiusCompensation() { } function writeFeed(feed, moveInZ, multiAxis) { if (useSimpleFeeds) { return; } var fCode = multiAxis ? "VS" : "MS"; if (multiAxis) { if (dpmFormat.getResultingValue(feed[0]) != dpmFormat.getResultingValue(dpmOutput1.getCurrent()) || dpmFormat.getResultingValue(feed[1]) != dpmFormat.getResultingValue(dpmOutput2.getCurrent())) { dpmOutput1.reset(); dpmOutput2.reset(); var f1 = dpmOutput1.format(feed[0]); var f2 = dpmOutput2.format(feed[1]); if (getProperty("fifthAxis")) { writeBlock(fCode, "", "", f1, f2); } else { writeBlock(fCode, "", "", "", f2); } feedOutput.reset(); } } else { var xyFeed = dpmAsXY ? feed[0] : feed; var zFeed = dpmAsXY ? feed[1] : feed; xyFeed = Math.max(xyFeed, 0.001 * 60); zFeed = Math.max(zFeed, 0.001 * 60); if (getProperty("SB3v36")) { var f = feedOutput.format(xyFeed); var f1 = feedFormat.areDifferent(zFeed, feedZOutput.getCurrent()); if (f || (moveInZ && f1)) { writeBlock(fCode, f, feedZOutput.format(zFeed)); if (!dpmAsXY) { dpmOutput1.reset(); dpmOutput2.reset(); previousDPMFeed[0] = 0; previousDPMFeed[1] = 0; } } } else { if (moveInZ) { // limit feed if moving in Z xyFeed = Math.min((xyFeed, maxZFeed)); } var f = feedOutput.format(xyFeed); if (f) { writeBlock(fCode, f, feedZOutput.format(Math.min(zFeed, maxZFeed))); if (!dpmAsXY) { dpmOutput1.reset(); dpmOutput2.reset(); previousDPMFeed[0] = 0; previousDPMFeed[1] = 0; } } } } dpmAsXY = false; } function onRapid(_x, _y, _z) { var x = xOutput.format(_x); var y = yOutput.format(_y); var z = zOutput.format(_z); if (x || y || z) { writeBlock("J3", x, y, z); } } function onLinear(_x, _y, _z, feed) { var x = xOutput.format(_x); var y = yOutput.format(_y); var z = zOutput.format(_z); writeFeed(feed, !!z, false); if (x || y || z) { writeBlock("M3", x, y, z); } } function getOptimizedHeads(_x, _y, _z, _a, _b, _c) { var xyz = new Vector(); if (getProperty("fifthAxis")) { var displacement = machineConfiguration.getDirection(new Vector(_a, _b, _c)); displacement.multiply(headOffset); // control will compensate for tool length displacement = Vector.diff(displacement, new Vector(0, 0, headOffset)); xyz.setX(_x + displacement.x); xyz.setY(_y + displacement.y); xyz.setZ(_z + displacement.z); } else { // don't adjust points for 4-axis machines xyz.setX(_x); xyz.setY(_y); xyz.setZ(_z); } return xyz; } 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; } var xyz = getOptimizedHeads(_x, _y, _z, _a, _b, _c); var x = xOutput.format2(xyz.x); var y = yOutput.format2(xyz.y); var z = zOutput.format2(xyz.z); var a = aOutput.format(_a); var b = bOutput.format(_b); writeBlock("J5", x, y, z, a, b); } 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; } var xyz = getOptimizedHeads(_x, _y, _z, _a, _b, _c); var x = xOutput.format2(xyz.x); var y = yOutput.format2(xyz.y); var z = zOutput.format2(xyz.z); var multiAxis = (aOutput.isEnabled() && abcFormat.areDifferent(_a, aOutput.getCurrent())) || (bOutput.isEnabled() && abcFormat.areDifferent(_b, bOutput.getCurrent())); var a = aOutput.format(_a); var b = bOutput.format(_b); if (x || y || z || a || b) { if (multiAxis) { var f = getMultiaxisFeed(_x, _y, _z, _a, _b, _c, feed); if (dpmAsXY) { writeFeed(f.frn, !!z, false); } else { writeFeed(f.frn, !!z, multiAxis); } writeBlock("M5", x, y, z, a, b); } else { writeFeed(feed, !!z, multiAxis); writeBlock("M3", x, y, z); } } } // Start of onRewindMachine logic /***** Be sure to add 'safeRetractDistance' to post getProperty(" ")*****/ var performRewinds = false; // enables the onRewindMachine logic var safeRetractFeed = (unit == IN) ? 20 : 500; var safePlungeFeed = (unit == IN) ? 10 : 250; var stockAllowance = new Vector(toPreciseUnit(0.1, IN), toPreciseUnit(0.1, IN), toPreciseUnit(0.1, IN)); /** Allow user to override the onRewind logic. */ function onRewindMachineEntry(_a, _b, _c) { return false; } /** Retract to safe position before indexing rotaries. */ function moveToSafeRetractPosition(isRetracted) { writeBlock( "JZ", zOutput.format(machineConfiguration.getRetractPlane()) ); writeBlock("JH"); if (getProperty("forceHomeOnIndexing")) { writeBlock( "JX", xOutput.format(machineConfiguration.getRetractPlane()), "JY", yOutput.format(machineConfiguration.getRetractPlane()) ); writeBlock("JH"); } } /** Return from safe position after indexing rotaries. */ function returnFromSafeRetractPosition(position, abc) { forceXYZ(); xOutput.reset(); yOutput.reset(); zOutput.reset(); onRapid5D(position.x, position.y, position.z, abc.x, abc.y, abc.z); //zOutput.enable(); //onExpandedRapid(position.x, position.y, position.z); } /** Intersect the point-vector with the stock box. */ function intersectStock(point, direction) { var intersection = getWorkpiece().getRayIntersection(point, direction, stockAllowance); return intersection === null ? undefined : intersection.second; } /** Calculates the retract point using the stock box and safe retract distance. */ function getRetractPosition(currentPosition, currentDirection) { var retractPos = intersectStock(currentPosition, currentDirection); if (retractPos == undefined) { if (tool.getFluteLength() != 0) { retractPos = Vector.sum(currentPosition, Vector.product(currentDirection, tool.getFluteLength())); } } if ((retractPos != undefined) && getProperty("safeRetractDistance")) { retractPos = Vector.sum(retractPos, Vector.product(currentDirection, getProperty("safeRetractDistance"))); } return retractPos; } /** Determines if the angle passed to onRewindMachine is a valid starting position. */ function isRewindAngleValid(_a, _b, _c) { // make sure the angles are different from the last output angles if (!abcFormat.areDifferent(getCurrentDirection().x, _a) && !abcFormat.areDifferent(getCurrentDirection().y, _b) && !abcFormat.areDifferent(getCurrentDirection().z, _c)) { error( localize("REWIND: Rewind angles are the same as the previous angles: ") + abcFormat.format(_a) + ", " + abcFormat.format(_b) + ", " + abcFormat.format(_c) ); return false; } // make sure angles are within the limits of the machine var abc = new Array(_a, _b, _c); var ix = machineConfiguration.getAxisU().getCoordinate(); var failed = false; if ((ix != -1) && !machineConfiguration.getAxisU().isSupported(abc[ix])) { failed = true; } ix = machineConfiguration.getAxisV().getCoordinate(); if ((ix != -1) && !machineConfiguration.getAxisV().isSupported(abc[ix])) { failed = true; } ix = machineConfiguration.getAxisW().getCoordinate(); if ((ix != -1) && !machineConfiguration.getAxisW().isSupported(abc[ix])) { failed = true; } if (failed) { error( localize("REWIND: Rewind angles are outside the limits of the machine: ") + abcFormat.format(_a) + ", " + abcFormat.format(_b) + ", " + abcFormat.format(_c) ); return false; } return true; } function onRewindMachine(_a, _b, _c) { if (!performRewinds) { error(localize("REWIND: Rewind of machine is required for simultaneous multi-axis toolpath and has been disabled.")); return; } // Allow user to override rewind logic if (onRewindMachineEntry(_a, _b, _c)) { return; } // Determine if input angles are valid or will cause a crash if (!isRewindAngleValid(_a, _b, _c)) { error( localize("REWIND: Rewind angles are invalid:") + abcFormat.format(_a) + ", " + abcFormat.format(_b) + ", " + abcFormat.format(_c) ); return; } // Work with the tool end point if (currentSection.getOptimizedTCPMode() == 0) { currentTool = getCurrentPosition(); } else { currentTool = machineConfiguration.getOrientation(getCurrentDirection()).multiply(getCurrentPosition()); } var currentABC = getCurrentDirection(); var currentDirection = machineConfiguration.getDirection(currentABC); // Calculate the retract position var retractPosition = getRetractPosition(currentTool, currentDirection); // Output warning that axes take longest route if (retractPosition == undefined) { error(localize("REWIND: Cannot calculate retract position.")); return; } else { var text = localize("REWIND: Tool is retracting due to rotary axes limits."); warning(text); writeComment(text); } // Move to retract position var position; if (currentSection.getOptimizedTCPMode() == 0) { position = retractPosition; } else { position = machineConfiguration.getOrientation(getCurrentDirection()).getTransposed().multiply(retractPosition); } onLinear5D(position.x, position.y, position.z, currentABC.x, currentABC.y, currentABC.z, safeRetractFeed); // Cancel 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 // writeBlock(gFormat.format(234), //hFormat.format(tool.lengthOffset), formatComment("TCPC ON")); // Move back to position above part var workpiece = getWorkpiece(); var partDatum = workpiece.lower.z; if (partDatum > 0) { writeln("&PWZorigin = Table Surface"); } else { writeln("&PWZorigin = Part Surface"); } if (currentSection.getOptimizedTCPMode() != 0) { position = machineConfiguration.getOrientation(new Vector(_a, _b, _c)).getTransposed().multiply(retractPosition); } returnFromSafeRetractPosition(position, new Vector(_a, _b, _c)); // Plunge tool back to original position if (currentSection.getOptimizedTCPMode() != 0) { currentTool = machineConfiguration.getOrientation(new Vector(_a, _b, _c)).getTransposed().multiply(currentTool); } onLinear5D(currentTool.x, currentTool.y, currentTool.z, _a, _b, _c, safePlungeFeed); } // End of onRewindMachine logic // 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 = new Array(0, 0); // previously output DPM feed var dpmFeedToler = 0.1 * 60; // tolerance to determine when the DPM feed has changed var dpmFeedMin = 0.002 * 60; // minimum DPM feed // 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, 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 !getProperty("fifthAxis"); // false for 5-axis and true for 4-axis } /** Calculate the DPM feedrate number. */ var dpmAsXY = false; // multi-axis feeds output as IPM feeds (true) or DPM feeds (false) function getFeedDPM(_moveLength, _feed) { dpmAsXY = false; if ((_feed == 0) || (_moveLength.tool < 0.0001) || (toDeg(_moveLength.abcLength) < 0.0005)) { previousDPMFeed[0] = 0; previousDPMFeed[1] = 0; return [_feed, _feed]; } var moveTime = _moveLength.tool / _feed; if (moveTime == 0) { return [_feed, _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 = new Array(_feed, _feed); } else if (false) { // standard DPM dpmFeed = Math.min(toDeg(_moveLength.abcLength) / moveTime, maxDPM); if (Math.abs(dpmFeed - previousDPMFeed[0]) < dpmFeedToler) { dpmFeed = previousDPMFeed[0]; } } 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[0]) < dpmFeedToler) { dpmFeed = previousDPMFeed[0]; } } else { // machine specific calculation var dpmA; var dpmB; var xy = new Vector(_moveLength.xyz.x, _moveLength.xyz.y, 0).length; if (getProperty("useDPMFeeds") == "false" && ((xyzFormat.getResultingValue(_moveLength.xyz.x) != 0) || (xyzFormat.getResultingValue(_moveLength.xyz.y) != 0) || (xyzFormat.getResultingValue(_moveLength.xyz.z) != 0))) { dpmA = xy / moveTime; dpmB = _moveLength.xyz.z / moveTime; dpmAsXY = true; } else if (getProperty("useDPMFeeds") == "tooltip") { dpmA = _feed; dpmB = _feed; dpmAsXY = true; } else { dpmA = toDeg(_moveLength.abc.x) / moveTime; dpmB = toDeg(_moveLength.abc.y) / moveTime; dpmA = Math.max(dpmA, dpmFeedMin); dpmB = Math.max(dpmB, dpmFeedMin); dpmAsXY = false; } dpmFeed = new Array(Math.min(dpmA, maxDPM), Math.min(dpmB, maxDPM)); if ((Math.abs(dpmFeed[0] - previousDPMFeed[0]) < dpmFeedToler) && (previousDPMFeed[0] != 0)) { dpmFeed[0] = previousDPMFeed[0]; } if ((Math.abs(dpmFeed[1] - previousDPMFeed[1]) < dpmFeedToler) && (previousDPMFeed[1] != 0)) { dpmFeed[1] = previousDPMFeed[1]; } } previousDPMFeed[0] = dpmFeed[0]; previousDPMFeed[1] = dpmFeed[1]; 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 start = getCurrentPosition(); if (isHelical()) { linearize(tolerance); return; } switch (getCircularPlane()) { case PLANE_XY: writeFeed(feed, false, false); writeBlock("CG", "", xOutput.format(x), yOutput.format(y), xyzFormat.format(cx - start.x), xyzFormat.format(cy - start.y), "", clockwise ? 1 : -1); break; default: linearize(tolerance); } } function onCommand(command) { switch (command) { case COMMAND_STOP_SPINDLE: if (getProperty("SB3v36")) { writeln("C7"); // call macro 7 } else { writeln("SO 1,0"); } break; case COMMAND_START_SPINDLE: if (getProperty("SB3v36")) { writeln("C6"); // call macro 6 } else { writeln("SO 1,1"); } writeln("PAUSE 2"); // wait for 2 seconds for spindle to ramp up break; } } function onSectionEnd() { xOutput.offset = 0; yOutput.offset = 0; zOutput.offset = 0; // 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(); } function onClose() { onCommand(COMMAND_STOP_SPINDLE); retracted = setWorkPlane(new Vector(0, 0, 0)); // reset working plane if (!retracted) { writeBlock( "JZ", zOutput.format(machineConfiguration.getRetractPlane()) ); writeBlock("JH"); } writeBlock("END"); writeln(""); writeln(""); writeBlock("UNIT_ERROR:"); writeBlock("CN, 91"); writeBlock("END"); } function setProperty(property, value) { properties[property].current = value; }