freecad-cam/Mod/cam-dev/ref-fusion/CAM360/Data/Posts/shopbot.cps

1259 lines
42 KiB
Plaintext

/**
Copyright (C) 2012-2025 by Autodesk, Inc.
All rights reserved.
ShopBot OpenSBP post processor configuration.
$Revision: 45355 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;
}