diff --git a/MPCNC.cps b/MPCNC.cps index 9bf0a32..119aaeb 100644 --- a/MPCNC.cps +++ b/MPCNC.cps @@ -67,7 +67,7 @@ properties = { mapD_RestoreFirstRapids: false, // Map first G01 --> G00 mapE_RestoreRapids: false, // Map G01 --> G00 for SafeTravelsAboveZ - mapF_SafeZ: 5, // G01 mapped to G00 if Z is >= jobSafeZRapid + mapF_SafeZ: "Retract:15", // G01 mapped to G00 if Z is >= jobSafeZRapid mapG_AllowRapidZ: false, // Allow G01 --> G00 for vertical retracts and Z descents above safe toolChange0_Enabled: false, // Enable tool change code (bultin tool change requires LCD display) @@ -200,8 +200,8 @@ propertyDefinitions = { type: "boolean", default_mm: false, default_in: false }, mapF_SafeZ: { - title: "Map: Safe Z to Rapid", description: "Must be above or equal to this value to map G1s --> G0s", group: 3, - type: "integer", default_mm: 10, default_in: 0.590551 + title: "Map: Safe Z to Rapid", description: "Must be above or equal to this value to map G1s --> G0s; constant or keyword (see docs)", group: 3, + type: "string", default_mm: "Retract:15", default_in: "Retract:15" }, mapG_AllowRapidZ: { title: "Map: Allow Rapid Z", description: "Enable to include vertical retracts and safe descents", group: 3, @@ -288,7 +288,6 @@ propertyDefinitions = { ] }, - gcodeStartFile: { title: "Extern: Start File", description: "File with custom Gcode for header/start (in nc folder)", group: 7, type: "file", default_mm: "", default_in: "" @@ -463,6 +462,174 @@ function flushMotions() { } } +//---------------- Safe Rapids ---------------- + +var eSafeZ = { + CONST: 0, + FEED: 1, + RETRACT: 2, + CLEARANCE: 3, + ERROR: 4, + prop: { + 0: {name: "Const", regex: /^\d+\.?\d*$/, numRegEx: /^(\d+\.?\d*)$/, value: 0}, + 1: {name: "Feed", regex: /^Feed:/i, numRegEx: /:(\d+\.?\d*)$/, value: 1}, + 2: {name: "Retract", regex: /^Retract:/i, numRegEx: /:(\d+\.?\d*)$/, alue: 2}, + 3: {name: "Clearance", regex: /^Clearance:/i, numRegEx: /:(\d+\.?\d*)$/, value: 3}, + 4: {name: "Error", regex: /^$/, numRegEx: /^$/, value: 4} + } +}; + +var safeZMode = eSafeZ.CONST; +var safeZHeightDefault = 15; +var safeZHeight; + +function parseSafeZProperty() { + var str = properties.mapF_SafeZ; + + // Look for either a number by itself or 'Feed:', 'Retract:' or 'Clearance:' + for (safeZMode = eSafeZ.CONST; safeZMode < eSafeZ.ERROR; safeZMode++) { + if (str.search(eSafeZ.prop[safeZMode].regex) == 0) { + break; + } + } + + // If it was not an error then get the number + if (safeZMode != eSafeZ.ERROR) { + safeZHeightDefault = str.match(eSafeZ.prop[safeZMode].numRegEx); + + if ((safeZHeightDefault == null) || (safeZHeightDefault.length !=2)) { + writeComment(eComment.Debug, " parseSafeZProperty: " + safeZHeightDefault); + writeComment(eComment.Debug, " parseSafeZProperty.length: " + (safeZHeightDefault != null? safeZHeightDefault.length : "na")); + writeComment(eComment.Debug, " parseSafeZProperty: Couldn't find number"); + safeZMode = eSafeZ.ERROR; + safeZHeightDefault = 15; + } + else { + safeZHeightDefault = safeZHeightDefault[1]; + } + } + + writeComment(eComment.Debug, " parseSafeZProperty: safeZMode = '" + eSafeZ.prop[safeZMode].name + "'"); + writeComment(eComment.Debug, " parseSafeZProperty: safeZHeightDefault = " + safeZHeightDefault); +} + +function safeZforSection(_section) +{ + if (properties.mapE_RestoreRapids) { + switch (safeZMode) { + case eSafeZ.CONST: + safeZHeight = safeZHeightDefault; + writeComment(eComment.Important, " SafeZ using const: " + safeZHeight); + break; + + case eSafeZ.FEED: + if (hasParameter("operation:feedHeight_value") && hasParameter("operation:feedHeight_absolute")) { + let feed = _section.getParameter("operation:feedHeight_value"); + let abs = _section.getParameter("operation:feedHeight_absolute"); + + if (abs == 1) { + safeZHeight = feed; + writeComment(eComment.Info, " SafeZ feed level: " + safeZHeight); + } + else { + safeZHeight = safeZHeightDefault; + writeComment(eComment.Important, " SafeZ feed level not abs: " + safeZHeight); + } + } + else { + safeZHeight = safeZHeightDefault; + writeComment(eComment.Important, " SafeZ feed level not defined: " + safeZHeight); + } + break; + + case eSafeZ.RETRACT: + if (hasParameter("operation:retractHeight_value") && hasParameter("operation:retractHeight_absolute")) { + let retract = _section.getParameter("operation:retractHeight_value"); + let abs = _section.getParameter("operation:retractHeight_absolute"); + + if (abs == 1) { + safeZHeight = retract; + writeComment(eComment.Info, " SafeZ retract level: " + safeZHeight); + } + else { + safeZHeight = safeZHeightDefault; + writeComment(eComment.Important, " SafeZ retract level not abs: " + safeZHeight); + } + } + else { + safeZHeight = safeZHeightDefault; + writeComment(eComment.Important, " SafeZ: retract level not defined: " + safeZHeight); + } + break; + + case eSafeZ.CLEARANCE: + if (hasParameter("operation:clearanceHeight_value") && hasParameter("operation:clearanceHeight_absolute")) { + var clearance = _section.getParameter("operation:clearanceHeight_value"); + let abs = _section.getParameter("operation:clearanceHeight_absolute"); + + if (abs == 1) { + safeZHeight = clearance; + writeComment(eComment.Info, " SafeZ clearance level: " + safeZHeight); + } + else { + safeZHeight = safeZHeightDefault; + writeComment(eComment.Important, " SafeZ clearance level not abs: " + safeZHeight); + } + } + else { + safeZHeight = safeZHeightDefault; + writeComment(eComment.Important, " SafeZ clearance level not defined: " + safeZHeight); + } + break; + + case eSafeZ.ERROR: + safeZHeight = safeZHeightDefault; + writeComment(eComment.Important, " >>> WARNING: " + propertyDefinitions.mapF_SafeZ.title + "format error: " + safeZHeight); + break; + } + } +} + + +// Returns true if the rules to convert G1s to G0s are satisfied +function isSafeToRapid(x, y, z) { + if (properties.mapE_RestoreRapids) { + //let zSafe = (z >= properties.mapF_SafeZ); + let zSafe = (z >= safeZHeight); + + // Destination z must be in safe zone. + if (zSafe) { + let cur = getCurrentPosition(); + let zConstant = (z == cur.z); + let zUp = (z > cur.z); + let xyConstant = ((x == cur.x) && (y == cur.y)); + let curZSafe = (cur.z >= properties.mapF_SafeZ); + + // Restore Rapids only when the target Z is safe and + // Case 1: Z is not changing, but XY are + // Case 2: Z is increasing, but XY constant + + // Z is not changing and we know we are in the safe zone + if (zConstant) { + return true; + } + + // We include moves of Z up as long as xy are constant + else if (properties.mapG_AllowRapidZ && zUp && xyConstant) { + return true; + } + + // We include moves of Z down as long as xy are constant and z always remains safe + else if (properties.mapG_AllowRapidZ && (!zUp) && xyConstant && curZSafe) { + return true; + } + } + } + + return false; +} + +//---------------- Coolant ---------------- // Coolant function CoolantA(on) { writeBlock(on ? properties.cl2_coolantAOn : properties.cl3_coolantAOff); @@ -476,22 +643,34 @@ function CoolantB(on) { function onOpen() { fw = properties.job0_SelectedFirmware; + // Output anything special to start the GCode if (fw == eFirmware.GRBL) { - gMotionModal = createModal({}, gFormat); // modal group 1 // G0-G3, ... writeln("%"); } + + // Configure the GCode G commands + if (fw == eFirmware.GRBL) { + gMotionModal = createModal({}, gFormat); // modal group 1 // G0-G3, ... + } else { gMotionModal = createModal({ force: true }, gFormat); // modal group 1 // G0-G3, ... } + // Configure how the feedrate is formatted if (properties.fr2_EnforceFeedrate) { fOutput = createVariable({ force: true }, fFormat); } + // Set the starting sequence number for line numbering sequenceNumber = properties.job6_SequenceNumberStart; + + // Set the seperator used between text if (!properties.job8_SeparateWordsWithSpace) { setWordSeparator(""); } + + // Determine the safeZHeight to do rapids + parseSafeZProperty(); } // Called at end of gcode file @@ -540,6 +719,9 @@ function onSection() { writeComment(eComment.Important, " *** SECTION begin ***"); + // Determine the Safe Z Height to map G1s to G0s + safeZforSection(currentSection); + // Do a tool change if tool changes are enabled and its not the first section and this section uses // a different tool then the previous section if (properties.toolChange0_Enabled && !isFirstSection() && tool.number != getPreviousSection().getTool().number) { @@ -640,42 +822,6 @@ function onRapid(x, y, z) { rapidMovements(x, y, z); } -function safeToRapid(x, y, z) { - if (properties.mapE_RestoreRapids) { - let zSafe = (z >= properties.mapF_SafeZ); - - // Destination z must be in safe zone. - if (zSafe) { - let cur = getCurrentPosition(); - let zConstant = (z == cur.z); - let zUp = (z > cur.z); - let xyConstant = ((x == cur.x) && (y == cur.y)); - let curZSafe = (cur.z >= properties.mapF_SafeZ); - - // Restore Rapids only when the target Z is safe and - // Case 1: Z is not changing, but XY are - // Case 2: Z is increasing, but XY constant - - // Z is not changing and we know we are in the safe zone - if (zConstant) { - return true; - } - - // We include moves of Z up as long as xy are constant - else if (properties.mapG_AllowRapidZ && zUp && xyConstant) { - return true; - } - - // We include moves of Z down as long as xy are constant and z always remains safe - else if (properties.mapG_AllowRapidZ && (!zUp) && xyConstant && curZSafe) { - return true; - } - } - } - - return false; -} - // Feed movements function onLinear(x, y, z, feed) { // If we are allowing Rapids to be recovered from Linear (cut) moves, which is @@ -693,7 +839,7 @@ function onLinear(x, y, z, feed) { forceSectionToStartWithRapid = false; onRapid(x, y, z); } - else if (safeToRapid(x, y, z)) { + else if (isSafeToRapid(x, y, z)) { writeComment(eComment.Important, " Safe G1 --> G0"); onRapid(x, y, z); @@ -1006,7 +1152,7 @@ function writeInformation() { writeComment(eComment.Info, " G1->G0 Mapping Properties:"); writeComment(eComment.Info, " Map: First G1 -> G0 Rapid = " + properties.mapD_RestoreFirstRapids); writeComment(eComment.Info, " Map: G1s -> G0 Rapids = " + properties.mapE_RestoreRapids); - writeComment(eComment.Info, " Map: Safe Z to Rapid = " + properties.mapF_SafeZ); + writeComment(eComment.Info, " Map: SafeZ Mode = " + eSafeZ.prop[safeZMode].name + " : default = " + safeZHeightDefault); writeComment(eComment.Info, " Map: Allow Rapid Z = " + properties.mapG_AllowRapidZ); writeComment(eComment.Info, " "); diff --git a/README.md b/README.md index 4a85d42..9bc2d99 100644 --- a/README.md +++ b/README.md @@ -34,6 +34,12 @@ Some design points: # Properties +> WARNING: If you are using the Fusion 360 for Personal Use license, formally know as the Fusion 360 Hobbyist license, please respect the [limitations of that license](https://knowledge.autodesk.com/support/fusion-360/learn-explore/caas/sfdcarticles/sfdcarticles/Fusion-360-Free-License-Changes.html). To remain compliant with that license set your [Feed: Travel Speed X/Y] and [Feed: Travel Speed Z] no faster then your machine's maximum cut feedrate (see Group 2 Properties). +> +>Fusion 360 for Personal Use restricts all moves not to exceed the maximum cut speed. This has been implemented not by reducing the speed of G0s but by changing all G0 (moves) to G1 (cut) commands. The side effect of this was to unintentionally introduce situations where tool dragging and/or work piece collisions occur, general at the start of jobs or after tool changes. +> +>You can choose to resolve these issues by enabling the selective mapping of G1s->G0s (see Group 3 Properties). Theses issues are resolved as the post processor implements G0 moves by doing first a move in Z and then a move in X,Y while a G1 cuts travel in X,Y,Z at the same time. + ## Group 1: Job Properties Use these properties to control overall aspects of the job. @@ -91,20 +97,23 @@ identified in forums as the tool being initially dragged across the work surface If [Map: G1s -> G0s] is true then allows G1 XY cut movements (i.e. no change in Z) that occur at a height greater or equal to [Map: Safe Z to Rapid] to be converted to G0 Rapids. -Note: this assumes that the top of material is 0 in F360 and that any Z above -[Map: Safe Z to Rapid] is a movement in the air and clear of obstacles. If top of material is not 0 -or there are holddown clamp then adjust [Map: Safe Z to Rapid] appropriately. +Note: this assumes that any Z above [Map: Safe Z to Rapid] is a movement in the air and clear of +obstacles. Can be defined as a number or one of F360's planes (Feed, Retract or Clearance). + +Map: Safe Z for Rapids may be defined as: +* As a constant numeric value - safe Z will then always be this value for all sections, or +* As a reference to a F360 Height - safe Z will then follow the Height defined within the operation's Height tab. Allowable Heights are: Feed, Retract, or Clearance. The Height must be followed by a ":" and then a numeric value. The value will be used if Height is not defined for a section. If [Map: Allow Rapid Z] is true then G1 Z cut movements that either move straight up and end above [Map: Safe Z to Rapid], or straight down with the start and end positions both above [Map: Safe Z to Rapid] are included. Only occurs if [Map: G1s -> G0s] is also true. -|Title|Description|Default| -|---|---|---| -Map: First G1 -> G0 Rapid|Converts the first G1 of a cut to G0 Rapid|**false**| -Map: G1s -> G0s|Allow G1 cuts to be converted to Rapid G0 moves when safe and appropriate.|**false**| -Map: Safe Z for Rapids|A G1 cut's Z must be >= to this to be mapped to a Rapid G0.|**10**| -Map: Allow Rapid Z|Include vertical cut if they are safe.|**false**| +|Title|Description|Default|Format| +|---|---|---|---| +Map: First G1 -> G0 Rapid|Converts the first G1 of a cut to G0 Rapid|**false**| | +Map: G1s -> G0s|Allow G1 cuts to be converted to Rapid G0 moves when safe and appropriate.|**false**| | +Map: Safe Z for Rapids|A G1 cut's Z must be >= to this to be mapped to a Rapid G0. Can be two formats (1) a number which will be used for all sections, or (2) a reference to F360's Height followed by a default if Height is not available.|**Retract:15** (use the Retract height and if not available 15)| \ or \:\; e.g. 10 or Retract:7 or Feed:5| +Map: Allow Rapid Z|Include the mapping of vertical cuts if they are safe.|**false**| ## Group 4: Tool change Properties