/** Copyright (C) 2012-2018 by Autodesk, Inc. All rights reserved. MultiCam ISO post processor configuration. $Revision: 42145 3ef6ef136f68132df4d932bf16f29ac1ec1b893b $ $Date: 2018-09-28 16:13:20 $ FORKID {2B4EEF1D-EEE3-46ed-87F8-CC90E06689A3} */ description = "MultiCam ISO"; vendor = "MultiCam"; vendorUrl = "http://www.multicam.com"; legal = "Copyright (C) 2012-2018 by Autodesk, Inc."; certificationLevel = 2; minimumRevision = 40783; longDescription = "Generic ISO post for MultiCam. You can set the property 'retractZlevel' to a non-zero value to make the machine retract to the given Z-level between operations."; extension = "cnc"; 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 // user-defined properties properties = { writeHeader: true, // write additional header information writeMachine: true, // write machine writeTools: true, // writes the tools showSequenceNumbers: true, // show sequence numbers showSequenceNumbersAtToolChange: false, // only show sequence numbers at tool change sequenceNumberStart: 10, // first sequence number sequenceNumberIncrement: 5, // increment for sequence numbers optionalStop: false, // optional stop separateWordsWithSpace: true, // specifies that the words should be separated with a white space retractZlevel: 0, // safe retract Z-level. 0 means disabled. useIPS: false, // Output IPM feed as IPS useCycles: true // enable to output drilling cycles }; // user-defined property definitions propertyDefinitions = { writeMachine: {title:"Write machine", description:"Output the machine settings in the header of the code.", group:0, type:"boolean"}, writeHeader: {title:"Write header information", description:"If enabled, additional header information will be output.", 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"}, showSequenceNumbersAtToolChange: {title:"Sequence number only on tool change", description:"If enabled, sequence numbers are only outputted when a toolchange is called.", 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"}, retractZlevel: {title:"Retract Z level", description:"Sets the safe retracts Z level. 0 = disabled.", type:"number"}, useIPS: {title:"Output IPM feed as IPS.", type:"boolean"}, useCycles: {title:"Use cycles", description:"Specifies if canned drilling cycles should be used.", type:"boolean"} }; var numberOfToolSlots = 9999; var WARNING_WORK_OFFSET = 0; var WARNING_COOLANT = 1; 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 xyzFormat = createFormat({decimals:(unit == MM ? 3 : 4), forceDecimal:true}); var zFormat = createFormat({decimals:(unit == MM ? 3 : 4), forceDecimal:true, scale:-1}); var feedFormat = createFormat({decimals:(unit == MM ? 1 : 2)}); var toolFormat = createFormat({decimals:0}); var rpmFormat = createFormat({decimals:0}); var secFormat = createFormat({decimals:3, forceDecimal:true}); // seconds - range 0.001-1000 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"}, zFormat); var feedOutput = createVariable({prefix:"F"}, feedFormat); var sOutput = createVariable({prefix:"S", force:true}, rpmFormat); var dOutput = createVariable({}, dFormat); // circular output var iOutput = createReferenceVariable({prefix:"I"}, xyzFormat); var jOutput = createReferenceVariable({prefix:"J"}, xyzFormat); var kOutput = createReferenceVariable({prefix:"K"}, zFormat); var gMotionModal = createModal({force:true}, 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 gCycleModal = createModal({force:true}, gFormat); // modal group 9 // G81, ... // collected state var sequenceNumber; var retracted = false; // specifies that the tool has been retracted to the safe plane /** Writes the specified block. */ function writeBlock() { if (!formatWords(arguments)) { return; } if (properties.showSequenceNumbers && !properties.showSequenceNumbersAtToolChange) { writeWords2("N" + sequenceNumber, arguments); sequenceNumber += properties.sequenceNumberIncrement; } else { writeWords(arguments); } } /** Output a comment. */ function writeComment(text) { writeln("//" + text); } function onOpen() { if (properties.retractZlevel != 0) { machineConfiguration.setRetractPlane(properties.retractZlevel); } if (!properties.separateWordsWithSpace) { setWordSeparator(""); } sequenceNumber = properties.sequenceNumberStart; writeBlock(mFormat.format(90)); if (programName) { writeComment(programName); } if (programComment) { writeComment(programComment); } // Write model and post details if (properties.writeHeader) { writeComment("Multicam Router"); if (hasGlobalParameter("generated-by")) { var value = getGlobalParameter("generated-by"); writeComment(value); } if (hasGlobalParameter("generated-at")) { var value = getGlobalParameter("generated-at"); var date = new Date(value + " UTC"); writeComment(date.toString()); } if (hasGlobalParameter("username")) { var value = getGlobalParameter("username"); writeComment(value); } if (hasGlobalParameter("document-path")) { var value = getGlobalParameter("document-path"); writeComment(value); } if (hasGlobalParameter("job-description")) { var value = getGlobalParameter("job-description"); writeComment(value); } } // 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") + "=" + zFormat.format(zRanges[tool.number].getMinimum()); } comment += " - " + getToolTypeName(tool.type); writeComment(comment); } } } writeBlock(gAbsIncModal.format(90)); // absolute coordinates writeBlock(gFormat.format(74)); // incremental arc mode writeBlock(gPlaneModal.format(17)); } function onComment(message) { writeComment(message); } /** Force output of X, Y, and Z. */ function forceXYZ() { xOutput.reset(); yOutput.reset(); zOutput.reset(); } /** Force output of X, Y, Z, and F on next output. */ function forceAny() { forceXYZ(); feedOutput.reset(); } function onParameter(name, value) { } function onSection() { var insertToolCall = isFirstSection() || currentSection.getForceToolChange && currentSection.getForceToolChange() || (tool.number != getPreviousSection().getTool().number); retracted = false; if (isFirstSection() || insertToolCall) { if (machineConfiguration.getRetractPlane() != 0) { // retract to safe plane writeRetract(Z); } } writeln(""); if (insertToolCall) { if ((tool.type == TOOL_TAP_LEFT_HAND) || (tool.type == TOOL_TAP_RIGHT_HAND)) { writeBlock(gFormat.format(98), "P126", "D1"); } onCommand(COMMAND_COOLANT_OFF); if (!isFirstSection() && properties.optionalStop) { onCommand(COMMAND_OPTIONAL_STOP); } if (tool.number > numberOfToolSlots) { warning(localize("Tool number exceeds maximum value.")); } if (properties.showSequenceNumbers && properties.showSequenceNumbersAtToolChange) { writeBlock("N" + sequenceNumber, gFormat.format(0), "T" + toolFormat.format(tool.number)); sequenceNumber += properties.sequenceNumberIncrement; } else { writeBlock(gFormat.format(0), "T" + toolFormat.format(tool.number)); } if (tool.comment) { writeComment(tool.comment); } var showToolZMin = false; if (showToolZMin) { if (is3D()) { var numberOfSections = getNumberOfSections(); var zRange = currentSection.getGlobalZRange(); var number = tool.number; for (var i = currentSection.getId() + 1; i < numberOfSections; ++i) { var section = getSection(i); if (section.getTool().number != number) { break; } zRange.expandToRange(section.getGlobalZRange()); } writeComment(localize("ZMIN") + "=" + zRange.getMinimum()); } } } if (insertToolCall || isFirstSection() || (rpmFormat.areDifferent(spindleSpeed, sOutput.getCurrent())) || (tool.clockwise != getPreviousSection().getTool().clockwise)) { if (spindleSpeed < 1) { error(localize("Spindle speed out of range.")); } if (spindleSpeed > 99999) { warning(localize("Spindle speed exceeds maximum value.")); } writeBlock(gFormat.format(97), sOutput.format(((tool.type == TOOL_TAP_LEFT_HAND) ? -1 : 1) * spindleSpeed)); } // wcs if (currentSection.workOffset != 0) { warningOnce(localize("Work offset is not supported."), WARNING_WORK_OFFSET); } forceXYZ(); if (tool.coolant != COOLANT_OFF) { warningOnce(localize("Coolant not supported."), WARNING_COOLANT); } forceAny(); { var remaining = currentSection.workPlane; if (!isSameDirection(remaining.forward, new Vector(0, 0, 1))) { error(localize("Tool orientation is not supported.")); return; } setRotation(remaining); } var initialPosition = getFramePosition(currentSection.getInitialPosition()); if (!retracted && !insertToolCall) { if (getCurrentPosition().z < initialPosition.z) { writeBlock(gMotionModal.format(0), zOutput.format(initialPosition.z)); } } if (insertToolCall) { gMotionModal.reset(); writeBlock(gPlaneModal.format(17)); writeBlock( gMotionModal.format(0), xOutput.format(initialPosition.x), yOutput.format(initialPosition.y) ); writeBlock(gMotionModal.format(0), zOutput.format(initialPosition.z)); } else { writeBlock( gAbsIncModal.format(90), gMotionModal.format(0), xOutput.format(initialPosition.x), yOutput.format(initialPosition.y) ); } writeBlock(mFormat.format(12)); } function onDwell(seconds) { if (seconds > 99999.999) { warning(localize("Dwelling time is out of range.")); } seconds = clamp(0.001, seconds, 99999.999); writeBlock(gFormat.format(4), "F" + secFormat.format(seconds)); } function onSpindleSpeed(spindleSpeed) { writeBlock(sOutput.format(spindleSpeed)); } 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.")); } writeBlock(gMotionModal.format(0), x, y, z); feedOutput.reset(); } } function onLinear(_x, _y, _z, feed) { // Convert IPM to IPS if (properties.useIPS) { feed = (feed/60); } // at least one axis is required 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 = feedOutput.format(feed); if (x || y || z) { if (pendingRadiusCompensation >= 0) { pendingRadiusCompensation = -1; writeBlock(gPlaneModal.format(17)); switch (radiusCompensation) { case RADIUS_COMPENSATION_LEFT: writeBlock(gFormat.format(41)); writeBlock(gMotionModal.format(1), x, y, z, f); break; case RADIUS_COMPENSATION_RIGHT: writeBlock(gFormat.format(42)); writeBlock(gMotionModal.format(1), x, y, z, f); break; default: writeBlock(gFormat.format(40)); writeBlock(gMotionModal.format(1), 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 feedOutput.reset(); // force feed on next line } else { writeBlock(gMotionModal.format(1), f); } } } function onCircular(clockwise, cx, cy, cz, x, y, z, feed) { if (properties.useIPS) { feed = (feed/60); } if (isHelical()) { var t = tolerance; if (hasParameter("operation:tolerance")) { t = getParameter("operation:tolerance"); } linearize(t); return; } // one of X/Y and I/J are required and likewise if (pendingRadiusCompensation >= 0) { error(localize("Radius compensation cannot be activated/deactivated for a circular move.")); return; } var start = getCurrentPosition(); switch (getCircularPlane()) { case PLANE_XY: writeBlock(gPlaneModal.format(17)); writeBlock(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), feedOutput.format(feed)); break; case PLANE_ZX: writeBlock(gPlaneModal.format(18)); writeBlock(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), feedOutput.format(feed)); break; case PLANE_YZ: writeBlock(gPlaneModal.format(19)); writeBlock(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), feedOutput.format(feed)); break; default: var t = tolerance; if (hasParameter("operation:tolerance")) { t = getParameter("operation:tolerance"); } linearize(t); } } function onCycle() { if (properties.useCycles) { writeBlock(gPlaneModal.format(17)); } } function getCommonCycle(x, y, z, r) { forceXYZ(); // force xyz on first drill hole of any cycle return [xOutput.format(x), yOutput.format(y), "Z" + xyzFormat.format(z), "R" + xyzFormat.format(r)]; } function onCyclePoint(x, y, z) { if (!isSameDirection(getRotation().forward, new Vector(0, 0, 1))) { expandCyclePoint(x, y, z); return; } if (!properties.useCycles) { expandCyclePoint(x, y, z); return; } if (isFirstCyclePoint()) { repositionToCycleClearance(cycle, x, y, z); } // return to initial Z which is clearance plane and set absolute mode var F = cycle.feedrate; var P = !cycle.dwell ? 0 : clamp(1, cycle.dwell * 1000, 99999999); // in milliseconds switch (cycleType) { case "drilling": writeBlock( gAbsIncModal.format(90), gFormat.format(83), getCommonCycle(x, y, cycle.retract - cycle.bottom, -cycle.retract), feedOutput.format(F) ); break; case "deep-drilling": writeBlock( gAbsIncModal.format(90), gFormat.format(83), getCommonCycle(x, y, cycle.retract - cycle.bottom, -cycle.retract), "D" + xyzFormat.format(cycle.incrementalDepth), feedOutput.format(F) ); break; case "counter-boring": if (P > 0) { expandCyclePoint(x, y, z); } else { if (isFirstCyclePoint()) { writeBlock( gAbsIncModal.format(90), gCycleModal.format(81), getCommonCycle(x, y, cycle.retract - cycle.bottom, cycle.retract), feedOutput.format(F) ); } else { writeBlock( gAbsIncModal.format(90), gCycleModal.format(81), xOutput.format(x), yOutput.format(y) ); } } break; case "tapping": case "left-tapping": // attention: spindle speed will be negative case "right-tapping": F = tool.getTappingFeedrate(); if (isFirstCyclePoint()) { writeBlock( gAbsIncModal.format(90), gCycleModal.format(84), getCommonCycle(x, y, cycle.retract - cycle.bottom, cycle.retract), feedOutput.format(F) ); } else { writeBlock( gAbsIncModal.format(90), gCycleModal.format(84), xOutput.format(x), yOutput.format(y) ); } break; default: expandCyclePoint(x, y, z); } } function onCycleEnd() { } var mapCommand = { COMMAND_STOP:0, COMMAND_OPTIONAL_STOP:1, COMMAND_END:2, COMMAND_SPINDLE_CLOCKWISE:3, COMMAND_SPINDLE_COUNTERCLOCKWISE:4, COMMAND_STOP_SPINDLE:5 }; function onCommand(command) { switch (command) { case COMMAND_COOLANT_OFF: return; // ignore case COMMAND_START_SPINDLE: onCommand(tool.clockwise ? COMMAND_SPINDLE_CLOCKWISE : COMMAND_SPINDLE_COUNTERCLOCKWISE); return; case COMMAND_LOCK_MULTI_AXIS: return; case COMMAND_UNLOCK_MULTI_AXIS: return; case COMMAND_BREAK_CONTROL: return; case COMMAND_TOOL_MEASURE: return; } var stringId = getCommandStringId(command); var mcode = mapCommand[stringId]; if (mcode != undefined) { writeBlock(mFormat.format(mcode)); } else { onUnsupportedCommand(command); } } function onSectionEnd() { writeBlock(mFormat.format(22)); forceAny(); } /** Output block to do safe retract and/or move to home position. */ function writeRetract() { if (arguments.length == 0) { error(localize("No axis specified for writeRetract().")); return; } var words = []; // store all retracted axes in an array for (var i = 0; i < arguments.length; ++i) { let instances = 0; // checks for duplicate retract calls for (var j = 0; j < arguments.length; ++j) { if (arguments[i] == arguments[j]) { ++instances; } } if (instances > 1) { // error if there are multiple retract calls for the same axis error(localize("Cannot retract the same axis twice in one line")); return; } switch (arguments[i]) { case X: words.push("X" + xyzFormat.format(machineConfiguration.hasHomePositionX() ? machineConfiguration.getHomePositionX() : 0)); break; case Y: words.push("Y" + xyzFormat.format(machineConfiguration.hasHomePositionY() ? machineConfiguration.getHomePositionY() : 0)); break; case Z: words.push("Z" + xyzFormat.format(machineConfiguration.getRetractPlane())); retracted = true; // specifies that the tool has been retracted to the safe plane break; default: error(localize("Bad axis specified for writeRetract().")); return; } } if (words.length > 0) { gMotionModal.reset(); writeBlock(gMotionModal.format(0), words); } zOutput.reset(); } function onClose() { writeln(""); onCommand(COMMAND_COOLANT_OFF); if (machineConfiguration.getRetractPlane() != 0) { writeRetract(Z); } // writeRetract(X, Y); onImpliedCommand(COMMAND_END); onImpliedCommand(COMMAND_STOP_SPINDLE); writeBlock(mFormat.format(2)); // stop program, spindle stop, coolant off }