diff --git a/src/Mod/Path/App/PathSegmentWalker.cpp b/src/Mod/Path/App/PathSegmentWalker.cpp index 32dd2e729d..5928033eaa 100644 --- a/src/Mod/Path/App/PathSegmentWalker.cpp +++ b/src/Mod/Path/App/PathSegmentWalker.cpp @@ -275,7 +275,7 @@ void PathSegmentWalker::walk(PathSegmentVisitor &cb, const Base::Vector3d &start // relative mode absolutecenter = false; - } else if ((name=="G81")||(name=="G82")||(name=="G83")||(name=="G84")||(name=="G85")||(name=="G86")||(name=="G89")){ + } else if ((name=="G73")||(name=="G81")||(name=="G82")||(name=="G83")||(name=="G84")||(name=="G85")||(name=="G86")||(name=="G89")){ // drill,tap,bore double r = 0; if (cmd.has("R")) diff --git a/src/Mod/Path/GCode-description.md b/src/Mod/Path/GCode-description.md index 800953c267..62492caa66 100644 --- a/src/Mod/Path/GCode-description.md +++ b/src/Mod/Path/GCode-description.md @@ -2,30 +2,31 @@ This is a documentation of all GCodes used by the FreeCAD Path Workbench -| Command | Description | Supported parameters | -| --- | --- | --- | -| G0, G00 | Rapid move | X,Y,Z,A,B,C | -| G1, G01 | Normal interpolated move | X,Y,Z,A,B,C | -| G2, G02 | Clockwise arc | X,Y,Z,A,B,C,I,J,K | -| G3, G03 | Counterclockwise arc | X,Y,Z,A,B,C,I,J,K | -| G40 | Turn off tool radius compensation | Radius compensation is done in FreeCAD | -| G41 | Tool radius compensation value | Radius compensation is done in FreeCAD | -| G42 | Tool radius compensation value | Radius compensation is done in FreeCAD | -| G43 | Tool length offset | | -| G44 | Tool length offset | | -| G53 | Machine coordinate system fixture | ??? | -| G54 | Scratchpad coordinate system fixture | ??? | -| G55 - G59.9 | Machine specific work offset fixtures relative to homing switches | ??? | -| G81 | Machine specific drill operation | X,Y,Z,R,Q | -| G82 | Machine specific drill operation | X,Y,Z,R,Q | -| G83 | Machine specific drill operation | X,Y,Z,R,Q | -| G90 | Absolute coordinates | | -| G91 | Relative coordinates | | -| G98 | Return to initial Z level in canned cycle | | -| G99 | Return to R level in canned cycle | | -| M0, M00 | Compulsory stop | | -| M1, M01 | Optional stop | | -| M3, M03 | Spindle on (clockwise rotation) | S\ | -| M4, M04 | Spindle on (counterclockwise rotation) | S\ | -| M6, M06 | Tool change | T\ | -| (\) | comment | | +| Command | Description | Supported parameters | +| --- | --- | --- | +| G0, G00 | Rapid move | X,Y,Z,A,B,C | +| G1, G01 | Normal interpolated move | X,Y,Z,A,B,C | +| G2, G02 | Clockwise arc | X,Y,Z,A,B,C,I,J,K | +| G3, G03 | Counterclockwise arc | X,Y,Z,A,B,C,I,J,K | +| G40 | Turn off tool radius compensation | Radius compensation is done in FreeCAD | +| G41 | Tool radius compensation value | Radius compensation is done in FreeCAD | +| G42 | Tool radius compensation value | Radius compensation is done in FreeCAD | +| G43 | Tool length offset | | +| G44 | Tool length offset | | +| G53 | Machine coordinate system fixture | ??? | +| G54 | Scratchpad coordinate system fixture | ??? | +| G55 - G59.9 | Machine specific work offset fixtures relative to homing switches | ??? | +| G73 | Machine specific drill operation | X,Y,Z,R,Q | +| G81 | Machine specific drill operation | X,Y,Z,R,Q | +| G82 | Machine specific drill operation | X,Y,Z,R,Q | +| G83 | Machine specific drill operation | X,Y,Z,R,Q | +| G90 | Absolute coordinates | | +| G91 | Relative coordinates | | +| G98 | Return to initial Z level in canned cycle | | +| G99 | Return to R level in canned cycle | | +| M0, M00 | Compulsory stop | | +| M1, M01 | Optional stop | | +| M3, M03 | Spindle on (clockwise rotation) | S\ | +| M4, M04 | Spindle on (counterclockwise rotation) | S\ | +| M6, M06 | Tool change | T\ | +| (\) | comment | | diff --git a/src/Mod/Path/Generators/drill_generator.py b/src/Mod/Path/Generators/drill_generator.py index ac9b6b1e63..9b82a4225a 100644 --- a/src/Mod/Path/Generators/drill_generator.py +++ b/src/Mod/Path/Generators/drill_generator.py @@ -38,7 +38,27 @@ else: PathLog.setLevel(PathLog.Level.INFO, PathLog.thisModule()) -def generate(edge, dwelltime=0.0, peckdepth=0.0, repeat=1, retractheight=None): +def generate(edge, dwelltime=0.0, peckdepth=0.0, repeat=1, retractheight=None, chipBreak=False): + """ + Generates Gcode for drilling a single hole. + + Takes as input an edge. It assumes the edge is trivial with just two vectors. + The edge must be aligned with the Z axes (Vector(0,0,1)) or it is an error. + + The first vertex of the edge will be the startpoint + The second vertex of the edge will be the endpoint. + All other vertices are ignored. + + additionally, you can pass in a dwelltime, peckdepth, and repeat value. + + These will result in appropriate G81,G82, and G83 codes. + + If chipBreak is True, the generator will produce G73 cycles instead of G83. + Chipbreaking cycles produce very small retracts to break the chip rather than + full retracts to clear chips from the hole. + http://linuxcnc.org/docs/html/gcode/g-code.html#gcode:g73 + + """ startPoint = edge.Vertexes[0].Point endPoint = edge.Vertexes[1].Point @@ -49,6 +69,9 @@ def generate(edge, dwelltime=0.0, peckdepth=0.0, repeat=1, retractheight=None): PathLog.debug(numpy.isclose(startPoint.sub(endPoint).y, 0, rtol=1e-05, atol=1e-06)) PathLog.debug(endPoint) + if dwelltime > 0.0 and peckdepth > 0.0: + raise ValueError("Peck and Dwell cannot be used together") + if repeat < 1: raise ValueError("repeat must be 1 or greater") @@ -79,6 +102,12 @@ def generate(edge, dwelltime=0.0, peckdepth=0.0, repeat=1, retractheight=None): cmdParams["Z"] = endPoint.z cmdParams["R"] = retractheight if retractheight is not None else startPoint.z + if repeat < 1: + raise ValueError("repeat must be 1 or greater") + + if not type(repeat) is int: + raise ValueError("repeat value must be an integer") + if repeat > 1: cmdParams["L"] = repeat @@ -89,7 +118,7 @@ def generate(edge, dwelltime=0.0, peckdepth=0.0, repeat=1, retractheight=None): else: cmd = "G81" else: - cmd = "G83" + cmd = "G73" if chipBreak else "G83" cmdParams["Q"] = peckdepth return [Path.Command(cmd, cmdParams)] diff --git a/src/Mod/Path/Gui/Resources/panels/PageOpDrillingEdit.ui b/src/Mod/Path/Gui/Resources/panels/PageOpDrillingEdit.ui index dbcad34842..9011c261ee 100644 --- a/src/Mod/Path/Gui/Resources/panels/PageOpDrillingEdit.ui +++ b/src/Mod/Path/Gui/Resources/panels/PageOpDrillingEdit.ui @@ -61,101 +61,106 @@ - - - - + + + + + Peck + + + + + + + false + + + Time + + + + + + + Extend Depth + + + + + + + false + + + + + + - Peck - - - - - - - false + None + + - Depth - - - - - - - false - - - - - - - false + Drill Tip + + - Retract + 2x Drill Tip - - - - - - false - - - - - - - Dwell - - - - - - - false - - - Time - - - - - - - false - - - - - - - Extend Depth - - - + + + - - - - None - - - - - Drill Tip - - - - - 2x Drill Tip - - - - - - + + + false + + + + + + + Dwell + + + + + + + false + + + Depth + + + + + + + false + + + Retract + + + + + + + false + + + + + + + Chip Break + + + + @@ -175,7 +180,7 @@ Gui::QuantitySpinBox - QDoubleSpinBox + QWidget
Gui/QuantitySpinBox.h
diff --git a/src/Mod/Path/PathScripts/PathArray.py b/src/Mod/Path/PathScripts/PathArray.py index 1088763ddf..295a34f72b 100644 --- a/src/Mod/Path/PathScripts/PathArray.py +++ b/src/Mod/Path/PathScripts/PathArray.py @@ -295,7 +295,7 @@ class PathArray: CmdMoveStraight = ["G1", "G01"] CmdMoveCW = ["G2", "G02"] CmdMoveCCW = ["G3", "G03"] - CmdDrill = ["G81", "G82", "G83"] + CmdDrill = ["G73", "G81", "G82", "G83"] CmdMoveArc = CmdMoveCW + CmdMoveCCW CmdMove = CmdMoveStraight + CmdMoveArc diff --git a/src/Mod/Path/PathScripts/PathDrilling.py b/src/Mod/Path/PathScripts/PathDrilling.py index fcb894c9ec..952f647e60 100644 --- a/src/Mod/Path/PathScripts/PathDrilling.py +++ b/src/Mod/Path/PathScripts/PathDrilling.py @@ -98,6 +98,15 @@ class ObjectDrilling(PathCircularHoleBase.ObjectOp): PathOp.FeatureBaseGeometry | PathOp.FeatureLocations | PathOp.FeatureCoolant ) + def onDocumentRestored(self, obj): + if not hasattr(obj, "chipBreakEnabled"): + obj.addProperty( + "App::PropertyBool", + "chipBreakEnabled", + "Drill", + QT_TRANSLATE_NOOP("App::Property", "Use chipbreaking"), + ) + def initCircularHoleOperation(self, obj): """initCircularHoleOperation(obj) ... add drilling specific properties to obj.""" obj.addProperty( @@ -115,6 +124,12 @@ class ObjectDrilling(PathCircularHoleBase.ObjectOp): "Drill", QT_TRANSLATE_NOOP("App::Property", "Enable pecking"), ) + obj.addProperty( + "App::PropertyBool", + "chipBreakEnabled", + "Drill", + QT_TRANSLATE_NOOP("App::Property", "Use chipbreaking"), + ) obj.addProperty( "App::PropertyFloat", "DwellTime", @@ -231,10 +246,16 @@ class ObjectDrilling(PathCircularHoleBase.ObjectOp): dwelltime = obj.DwellTime if obj.DwellEnabled else 0.0 peckdepth = obj.PeckDepth.Value if obj.PeckEnabled else 0.0 repeat = 1 # technical debt: Add a repeat property for user control + chipBreak = (obj.chipBreakEnabled and obj.PeckEnabled) try: drillcommands = generator.generate( - edge, dwelltime, peckdepth, repeat, obj.RetractHeight.Value + edge, + dwelltime, + peckdepth, + repeat, + obj.RetractHeight.Value, + chipBreak=chipBreak ) except ValueError as e: # any targets that fail the generator are ignored diff --git a/src/Mod/Path/PathScripts/PathDrillingGui.py b/src/Mod/Path/PathScripts/PathDrillingGui.py index 30a594231b..64c093c66e 100644 --- a/src/Mod/Path/PathScripts/PathDrillingGui.py +++ b/src/Mod/Path/PathScripts/PathDrillingGui.py @@ -57,14 +57,17 @@ class TaskPanelOpPage(PathCircularHoleBaseGui.TaskPanelOpPage): self.dwellTimeSpinBox = PathGui.QuantitySpinBox( self.form.dwellTime, obj, "DwellTime" ) + self.form.chipBreakEnabled.setEnabled(False) def registerSignalHandlers(self, obj): self.form.peckEnabled.toggled.connect(self.form.peckDepth.setEnabled) self.form.peckEnabled.toggled.connect(self.form.dwellEnabled.setDisabled) + self.form.peckEnabled.toggled.connect(self.setChipBreakControl) self.form.dwellEnabled.toggled.connect(self.form.dwellTime.setEnabled) self.form.dwellEnabled.toggled.connect(self.form.dwellTimelabel.setEnabled) self.form.dwellEnabled.toggled.connect(self.form.peckEnabled.setDisabled) + self.form.dwellEnabled.toggled.connect(self.setChipBreakControl) self.form.peckRetractHeight.setEnabled(True) self.form.retractLabel.setEnabled(True) @@ -73,10 +76,17 @@ class TaskPanelOpPage(PathCircularHoleBaseGui.TaskPanelOpPage): self.form.dwellEnabled.setEnabled(False) self.form.peckDepth.setEnabled(True) self.form.peckDepthLabel.setEnabled(True) + self.form.chipBreakEnabled.setEnabled(True) elif self.form.dwellEnabled.isChecked(): self.form.peckEnabled.setEnabled(False) self.form.dwellTime.setEnabled(True) self.form.dwellTimelabel.setEnabled(True) + self.form.chipBreakEnabled.setEnabled(False) + else: + self.form.chipBreakEnabled.setEnabled(False) + + def setChipBreakControl(self): + self.form.chipBreakEnabled.setEnabled(self.form.peckEnabled.isChecked()) def getForm(self): """getForm() ... return UI""" @@ -104,6 +114,8 @@ class TaskPanelOpPage(PathCircularHoleBaseGui.TaskPanelOpPage): obj.DwellEnabled = self.form.dwellEnabled.isChecked() if obj.PeckEnabled != self.form.peckEnabled.isChecked(): obj.PeckEnabled = self.form.peckEnabled.isChecked() + if obj.chipBreakEnabled != self.form.chipBreakEnabled.isChecked(): + obj.chipBreakEnabled = self.form.chipBreakEnabled.isChecked() if obj.ExtraOffset != str(self.form.ExtraOffset.currentData()): obj.ExtraOffset = str(self.form.ExtraOffset.currentData()) @@ -124,6 +136,12 @@ class TaskPanelOpPage(PathCircularHoleBaseGui.TaskPanelOpPage): self.form.peckEnabled.setCheckState(QtCore.Qt.Checked) else: self.form.peckEnabled.setCheckState(QtCore.Qt.Unchecked) + self.form.chipBreakEnabled.setEnabled(False) + + if obj.chipBreakEnabled: + self.form.chipBreakEnabled.setCheckState(QtCore.Qt.Checked) + else: + self.form.chipBreakEnabled.setCheckState(QtCore.Qt.Unchecked) self.selectInComboBox(obj.ExtraOffset, self.form.ExtraOffset) @@ -139,6 +157,7 @@ class TaskPanelOpPage(PathCircularHoleBaseGui.TaskPanelOpPage): signals.append(self.form.dwellTime.editingFinished) signals.append(self.form.dwellEnabled.stateChanged) signals.append(self.form.peckEnabled.stateChanged) + signals.append(self.form.chipBreakEnabled.stateChanged) signals.append(self.form.toolController.currentIndexChanged) signals.append(self.form.coolantController.currentIndexChanged) signals.append(self.form.ExtraOffset.currentIndexChanged) diff --git a/src/Mod/Path/PathScripts/PathGeom.py b/src/Mod/Path/PathScripts/PathGeom.py index 2d9ea687c3..1486c73496 100644 --- a/src/Mod/Path/PathScripts/PathGeom.py +++ b/src/Mod/Path/PathScripts/PathGeom.py @@ -87,7 +87,7 @@ CmdMoveRapid = ["G0", "G00"] CmdMoveStraight = ["G1", "G01"] CmdMoveCW = ["G2", "G02"] CmdMoveCCW = ["G3", "G03"] -CmdMoveDrill = ["G81", "G82", "G83"] +CmdMoveDrill = ["G73", "G81", "G82", "G83"] CmdMoveArc = CmdMoveCW + CmdMoveCCW CmdMove = CmdMoveStraight + CmdMoveArc + CmdMoveDrill CmdMoveAll = CmdMove + CmdMoveRapid diff --git a/src/Mod/Path/PathScripts/PathSimulatorGui.py b/src/Mod/Path/PathScripts/PathSimulatorGui.py index f2cdb36c82..ba712b6092 100644 --- a/src/Mod/Path/PathScripts/PathSimulatorGui.py +++ b/src/Mod/Path/PathScripts/PathSimulatorGui.py @@ -290,7 +290,7 @@ class PathSimulation: if cmd.Name in ["G80"]: self.firstDrill = True - if cmd.Name in ["G81", "G82", "G83"]: + if cmd.Name in ["G73", "G81", "G82", "G83"]: if self.firstDrill: extendcommand = Path.Command("G0", {"Z": cmd.r}) self.curpos = self.RapidMove(extendcommand, self.curpos) @@ -372,7 +372,7 @@ class PathSimulation: ) = self.voxSim.GetResultMesh() if cmd.Name in ["G80"]: self.firstDrill = True - if cmd.Name in ["G81", "G82", "G83"]: + if cmd.Name in ["G73", "G81", "G82", "G83"]: extendcommands = [] if self.firstDrill: extendcommands.append(Path.Command("G0", {"Z": cmd.r})) diff --git a/src/Mod/Path/PathTests/TestPathDrillGenerator.py b/src/Mod/Path/PathTests/TestPathDrillGenerator.py index ef8d814304..f3c94bac61 100644 --- a/src/Mod/Path/PathTests/TestPathDrillGenerator.py +++ b/src/Mod/Path/PathTests/TestPathDrillGenerator.py @@ -152,3 +152,27 @@ class TestPathDrillGenerator(PathTestUtils.PathTestBase): self.assertRaises(ValueError, generator.generate, **args) args = {"edge": e, "retractheight": "1"} self.assertRaises(ValueError, generator.generate, **args) + + def test50(self): + """Test Error if dwell and peck""" + v1 = FreeCAD.Vector(0, 0, 10) + v2 = FreeCAD.Vector(0, 0, 0) + + e = Part.makeLine(v1, v2) + + # dwelltime should be a float + args = {"edge": e, "dwelltime": 1.0, "peckdepth": 1.0} + self.assertRaises(ValueError, generator.generate, **args) + + def test60(self): + """Test chipBreak""" + v1 = FreeCAD.Vector(0, 0, 10) + v2 = FreeCAD.Vector(0, 0, 0) + + e = Part.makeLine(v1, v2) + + args = {"edge": e, "peckdepth": 1.0, "chipBreak": True} + result = generator.generate(**args) + command = result[0] + + self.assertTrue(command.Name == "G73")