| | |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| |
|
| |
|
| | import FreeCAD |
| | import Part |
| | import Path |
| | import Path.Base.FeedRate as PathFeedRate |
| | import Path.Base.Generator.drill as drill |
| | import Path.Base.Generator.linking as linking |
| | import Path.Base.MachineState as PathMachineState |
| | import Path.Op.Base as PathOp |
| | import Path.Op.CircularHoleBase as PathCircularHoleBase |
| | import PathScripts.PathUtils as PathUtils |
| | from PySide.QtCore import QT_TRANSLATE_NOOP |
| |
|
| | __title__ = "CAM Drilling Operation" |
| | __author__ = "sliptonic (Brad Collette)" |
| | __url__ = "https://www.freecad.org" |
| | __doc__ = "CAM Drilling operation." |
| | __contributors__ = "IMBack!" |
| |
|
| | if False: |
| | Path.Log.setLevel(Path.Log.Level.DEBUG, Path.Log.thisModule()) |
| | Path.Log.trackModule(Path.Log.thisModule()) |
| | else: |
| | Path.Log.setLevel(Path.Log.Level.INFO, Path.Log.thisModule()) |
| |
|
| | translate = FreeCAD.Qt.translate |
| |
|
| |
|
| | class ObjectDrilling(PathCircularHoleBase.ObjectOp): |
| | """Proxy object for Drilling operation.""" |
| |
|
| | @classmethod |
| | def propertyEnumerations(self, dataType="data"): |
| | """helixOpPropertyEnumerations(dataType="data")... return property enumeration lists of specified dataType. |
| | Args: |
| | dataType = 'data', 'raw', 'translated' |
| | Notes: |
| | 'data' is list of internal string literals used in code |
| | 'raw' is list of (translated_text, data_string) tuples |
| | 'translated' is list of translated string literals |
| | """ |
| |
|
| | |
| | enums = { |
| | "ExtraOffset": [ |
| | (translate("CAM_Drilling", "None"), "None"), |
| | (translate("CAM_Drilling", "Drill Tip"), "Drill Tip"), |
| | (translate("CAM_Drilling", "2x Drill Tip"), "2x Drill Tip"), |
| | ], |
| | } |
| |
|
| | if dataType == "raw": |
| | return enums |
| |
|
| | data = list() |
| | idx = 0 if dataType == "translated" else 1 |
| |
|
| | Path.Log.debug(enums) |
| |
|
| | for k, v in enumerate(enums): |
| | data.append((v, [tup[idx] for tup in enums[v]])) |
| | Path.Log.debug(data) |
| |
|
| | return data |
| |
|
| | def circularHoleFeatures(self, obj): |
| | """circularHoleFeatures(obj) ... drilling works on anything, turn on all Base geometries and Locations.""" |
| | return PathOp.FeatureBaseGeometry | PathOp.FeatureLocations | PathOp.FeatureCoolant |
| |
|
| | def onDocumentRestored(self, obj): |
| | if hasattr(obj, "chipBreakEnabled"): |
| | obj.renameProperty("chipBreakEnabled", "ChipBreakEnabled") |
| | elif not hasattr(obj, "ChipBreakEnabled"): |
| | obj.addProperty( |
| | "App::PropertyBool", |
| | "ChipBreakEnabled", |
| | "Drill", |
| | QT_TRANSLATE_NOOP("App::Property", "Use chipbreaking"), |
| | ) |
| |
|
| | if hasattr(obj, "feedRetractEnabled"): |
| | obj.renameProperty("feedRetractEnabled", "FeedRetractEnabled") |
| | elif not hasattr(obj, "FeedRetractEnabled"): |
| | obj.addProperty( |
| | "App::PropertyBool", |
| | "FeedRetractEnabled", |
| | "Drill", |
| | QT_TRANSLATE_NOOP("App::Property", "Use G85 boring cycle with feed out"), |
| | ) |
| |
|
| | if hasattr(obj, "RetractMode"): |
| | obj.removeProperty("RetractMode") |
| |
|
| | |
| | if hasattr(obj, "RetractHeight"): |
| | |
| | if obj.RetractHeight.Value > obj.StartDepth.Value: |
| | Path.Log.warning( |
| | f"Migrating RetractHeight ({obj.RetractHeight.Value}) to StartDepth. " |
| | f"Old StartDepth was {obj.StartDepth.Value}" |
| | ) |
| | obj.StartDepth = obj.RetractHeight.Value |
| | obj.removeProperty("RetractHeight") |
| |
|
| | if not hasattr(obj, "KeepToolDown"): |
| | obj.addProperty( |
| | "App::PropertyBool", |
| | "KeepToolDown", |
| | "Drill", |
| | QT_TRANSLATE_NOOP( |
| | "App::Property", |
| | "Apply G99 retraction: only retract to StartDepth between holes in this operation", |
| | ), |
| | ) |
| |
|
| | def initCircularHoleOperation(self, obj): |
| | """initCircularHoleOperation(obj) ... add drilling specific properties to obj.""" |
| | obj.addProperty( |
| | "App::PropertyLength", |
| | "PeckDepth", |
| | "Drill", |
| | QT_TRANSLATE_NOOP( |
| | "App::Property", |
| | "Incremental Drill depth before retracting to clear chips", |
| | ), |
| | ) |
| | obj.addProperty( |
| | "App::PropertyBool", |
| | "PeckEnabled", |
| | "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", |
| | "Drill", |
| | QT_TRANSLATE_NOOP("App::Property", "The time to dwell between peck cycles"), |
| | ) |
| | obj.addProperty( |
| | "App::PropertyBool", |
| | "DwellEnabled", |
| | "Drill", |
| | QT_TRANSLATE_NOOP("App::Property", "Enable dwell"), |
| | ) |
| | obj.addProperty( |
| | "App::PropertyBool", |
| | "AddTipLength", |
| | "Drill", |
| | QT_TRANSLATE_NOOP( |
| | "App::Property", |
| | "Calculate the tip length and subtract from final depth", |
| | ), |
| | ) |
| | obj.addProperty( |
| | "App::PropertyEnumeration", |
| | "ExtraOffset", |
| | "Drill", |
| | QT_TRANSLATE_NOOP("App::Property", "How far the drilling depth is extended"), |
| | ) |
| | obj.addProperty( |
| | "App::PropertyBool", |
| | "KeepToolDown", |
| | "Drill", |
| | QT_TRANSLATE_NOOP( |
| | "App::Property", |
| | "Apply G99 retraction: only retract to StartDepth between holes in this operation", |
| | ), |
| | ) |
| | obj.addProperty( |
| | "App::PropertyBool", |
| | "FeedRetractEnabled", |
| | "Drill", |
| | QT_TRANSLATE_NOOP("App::Property", "Use G85 boring cycle with feed out"), |
| | ) |
| |
|
| | for n in self.propertyEnumerations(): |
| | setattr(obj, n[0], n[1]) |
| |
|
| | def circularHoleExecute(self, obj, holes): |
| | """circularHoleExecute(obj, holes) ... generate drill operation for each hole in holes.""" |
| | Path.Log.track() |
| | machinestate = PathMachineState.MachineState() |
| | |
| |
|
| | mode = "G99" if obj.KeepToolDown else "G98" |
| |
|
| | |
| | safe_height = obj.SafeHeight.Value |
| | if safe_height > obj.ClearanceHeight.Value: |
| | Path.Log.warning( |
| | f"SafeHeight ({safe_height}) is above ClearanceHeight ({obj.ClearanceHeight.Value}). " |
| | f"Using ClearanceHeight instead." |
| | ) |
| | safe_height = obj.ClearanceHeight.Value |
| |
|
| | |
| | endoffset = 0.0 |
| | if obj.ExtraOffset == "Drill Tip": |
| | endoffset = PathUtils.drillTipLength(self.tool) |
| | elif obj.ExtraOffset == "2x Drill Tip": |
| | endoffset = PathUtils.drillTipLength(self.tool) * 2 |
| |
|
| | |
| | edgelist = [] |
| | for hole in holes: |
| | v1 = FreeCAD.Vector(hole["x"], hole["y"], obj.StartDepth.Value) |
| | v2 = FreeCAD.Vector(hole["x"], hole["y"], obj.FinalDepth.Value - endoffset) |
| | edgelist.append(Part.makeLine(v1, v2)) |
| |
|
| | |
| | |
| | solids = [] |
| | for base in self.job.Model.Group: |
| | solids.append(base.Shape) |
| |
|
| | |
| |
|
| | |
| | |
| | |
| | |
| |
|
| | |
| | self.commandlist.append(Path.Command("(Begin Drilling)")) |
| |
|
| | |
| | command = Path.Command("G0", {"Z": obj.ClearanceHeight.Value}) |
| | machinestate.addCommand(command) |
| |
|
| | |
| | self.commandlist.append(command) |
| |
|
| | |
| | firstMove = True |
| | for edge in edgelist: |
| | Path.Log.debug(edge) |
| |
|
| | |
| | startPoint = edge.Vertexes[0].Point |
| |
|
| | |
| | if firstMove: |
| | command = Path.Command("G0", {"X": startPoint.x, "Y": startPoint.y}) |
| | self.commandlist.append(command) |
| | machinestate.addCommand(command) |
| | command = Path.Command("G0", {"Z": safe_height}) |
| | self.commandlist.append(command) |
| | machinestate.addCommand(command) |
| | firstMove = False |
| |
|
| | else: |
| | |
| | |
| | current_pos = machinestate.getPosition() |
| | target_at_retract_plane = FreeCAD.Vector(startPoint.x, startPoint.y, current_pos.z) |
| |
|
| | |
| | collision_detected = linking.check_collision( |
| | start_position=current_pos, |
| | target_position=target_at_retract_plane, |
| | solids=solids, |
| | ) |
| |
|
| | if collision_detected: |
| | |
| | |
| | target_at_safe_height = FreeCAD.Vector(startPoint.x, startPoint.y, safe_height) |
| | linking_moves = linking.get_linking_moves( |
| | start_position=current_pos, |
| | target_position=target_at_safe_height, |
| | local_clearance=safe_height, |
| | global_clearance=obj.ClearanceHeight.Value, |
| | tool_shape=self.tool.Shape, |
| | solids=solids, |
| | ) |
| | self.commandlist.extend(linking_moves) |
| | for move in linking_moves: |
| | machinestate.addCommand(move) |
| | |
| |
|
| | |
| | dwelltime = obj.DwellTime if obj.DwellEnabled else 0.0 |
| | peckdepth = obj.PeckDepth.Value if obj.PeckEnabled else 0.0 |
| | repeat = 1 |
| | chipBreak = obj.ChipBreakEnabled and obj.PeckEnabled |
| |
|
| | |
| | z_before_cycle = machinestate.Z |
| |
|
| | try: |
| | drillcommands = drill.generate( |
| | edge, |
| | dwelltime, |
| | peckdepth, |
| | repeat, |
| | obj.StartDepth.Value, |
| | chipBreak=chipBreak, |
| | feedRetract=obj.FeedRetractEnabled, |
| | ) |
| |
|
| | except ValueError as e: |
| | Path.Log.info(e) |
| | continue |
| |
|
| | |
| | for command in drillcommands: |
| | annotations = command.Annotations |
| | annotations["RetractMode"] = mode |
| | command.Annotations = annotations |
| | self.commandlist.append(command) |
| | machinestate.addCommand(command) |
| |
|
| | |
| | |
| | |
| | if mode == "G98": |
| | machinestate.Z = z_before_cycle |
| | else: |
| | machinestate.Z = obj.StartDepth.Value |
| |
|
| | |
| | PathFeedRate.setFeedRate(self.commandlist, obj.ToolController) |
| |
|
| | def opSetDefaultValues(self, obj, job): |
| | """opSetDefaultValues(obj, job) ... set default values for drilling operation""" |
| | obj.ExtraOffset = "None" |
| | obj.KeepToolDown = False |
| |
|
| | if hasattr(job.SetupSheet, "PeckDepth"): |
| | obj.PeckDepth = job.SetupSheet.PeckDepth |
| | elif self.applyExpression(obj, "PeckDepth", "OpToolDiameter*0.75"): |
| | obj.PeckDepth = 1 |
| |
|
| | if hasattr(job.SetupSheet, "DwellTime"): |
| | obj.DwellTime = job.SetupSheet.DwellTime |
| | else: |
| | obj.DwellTime = 1 |
| |
|
| |
|
| | def SetupProperties(): |
| | setup = [] |
| | setup.append("PeckDepth") |
| | setup.append("PeckEnabled") |
| | setup.append("DwellTime") |
| | setup.append("DwellEnabled") |
| | setup.append("AddTipLength") |
| | setup.append("ExtraOffset") |
| | setup.append("KeepToolDown") |
| | return setup |
| |
|
| |
|
| | def Create(name, obj=None, parentJob=None): |
| | """Create(name) ... Creates and returns a Drilling operation.""" |
| | if obj is None: |
| | obj = FreeCAD.ActiveDocument.addObject("Path::FeaturePython", name) |
| |
|
| | obj.Proxy = ObjectDrilling(obj, name, parentJob) |
| | if obj.Proxy: |
| | obj.Proxy.findAllHoles(obj) |
| |
|
| | return obj |
| |
|