Merge pull request #5330 from sliptonic/bug/translation-post
[Path] Bug/translation post
This commit is contained in:
@@ -20,7 +20,7 @@
|
||||
# * *
|
||||
# ***************************************************************************
|
||||
|
||||
''' Post Process command that will make use of the Output File and Post Processor entries in PathJob '''
|
||||
""" Post Process command that will make use of the Output File and Post Processor entries in PathJob """
|
||||
|
||||
from __future__ import print_function
|
||||
|
||||
@@ -37,17 +37,13 @@ import os
|
||||
from PathScripts.PathPostProcessor import PostProcessor
|
||||
from PySide import QtCore, QtGui
|
||||
from datetime import datetime
|
||||
from PySide.QtCore import QT_TRANSLATE_NOOP
|
||||
|
||||
LOG_MODULE = PathLog.thisModule()
|
||||
|
||||
PathLog.setLevel(PathLog.Level.INFO, LOG_MODULE)
|
||||
|
||||
|
||||
# Qt translation handling
|
||||
def translate(context, text, disambig=None):
|
||||
return QtCore.QCoreApplication.translate(context, text, disambig)
|
||||
|
||||
|
||||
class _TempObject:
|
||||
# pylint: disable=no-init
|
||||
Path = None
|
||||
@@ -57,14 +53,15 @@ class _TempObject:
|
||||
|
||||
|
||||
class DlgSelectPostProcessor:
|
||||
|
||||
def __init__(self, parent=None):
|
||||
# pylint: disable=unused-argument
|
||||
self.dialog = FreeCADGui.PySideUic.loadUi(":/panels/DlgSelectPostProcessor.ui")
|
||||
firstItem = None
|
||||
for post in PathPreferences.allEnabledPostProcessors():
|
||||
item = QtGui.QListWidgetItem(post)
|
||||
item.setFlags(QtCore.Qt.ItemFlag.ItemIsSelectable | QtCore.Qt.ItemFlag.ItemIsEnabled)
|
||||
item.setFlags(
|
||||
QtCore.Qt.ItemFlag.ItemIsSelectable | QtCore.Qt.ItemFlag.ItemIsEnabled
|
||||
)
|
||||
self.dialog.lwPostProcessor.addItem(item)
|
||||
if not firstItem:
|
||||
firstItem = item
|
||||
@@ -103,50 +100,52 @@ class CommandPathPost:
|
||||
path = job.PostProcessorOutputFile
|
||||
filename = path
|
||||
|
||||
if '%D' in filename:
|
||||
if "%D" in filename:
|
||||
D = FreeCAD.ActiveDocument.FileName
|
||||
if D:
|
||||
D = os.path.dirname(D)
|
||||
# in case the document is in the current working directory
|
||||
if not D:
|
||||
D = '.'
|
||||
D = "."
|
||||
else:
|
||||
FreeCAD.Console.PrintError("Please save document in order to resolve output path!\n")
|
||||
FreeCAD.Console.PrintError(
|
||||
"Please save document in order to resolve output path!\n"
|
||||
)
|
||||
return None
|
||||
filename = filename.replace('%D', D)
|
||||
filename = filename.replace("%D", D)
|
||||
|
||||
if '%d' in filename:
|
||||
if "%d" in filename:
|
||||
d = FreeCAD.ActiveDocument.Label
|
||||
filename = filename.replace('%d', d)
|
||||
filename = filename.replace("%d", d)
|
||||
|
||||
if '%j' in filename:
|
||||
if "%j" in filename:
|
||||
j = job.Label
|
||||
filename = filename.replace('%j', j)
|
||||
filename = filename.replace("%j", j)
|
||||
|
||||
if '%M' in filename:
|
||||
if "%M" in filename:
|
||||
pref = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Macro")
|
||||
M = pref.GetString("MacroPath", FreeCAD.getUserAppDataDir())
|
||||
filename = filename.replace('%M', M)
|
||||
filename = filename.replace("%M", M)
|
||||
|
||||
if '%s' in filename:
|
||||
if "%s" in filename:
|
||||
if job.SplitOutput:
|
||||
filename = filename.replace('%s', '_'+str(self.subpart))
|
||||
filename = filename.replace("%s", "_" + str(self.subpart))
|
||||
self.subpart += 1
|
||||
else:
|
||||
filename = filename.replace('%s', '')
|
||||
filename = filename.replace("%s", "")
|
||||
|
||||
policy = PathPreferences.defaultOutputPolicy()
|
||||
|
||||
openDialog = policy == 'Open File Dialog'
|
||||
openDialog = policy == "Open File Dialog"
|
||||
if os.path.isdir(filename) or not os.path.isdir(os.path.dirname(filename)):
|
||||
# Either the entire filename resolves into a directory or the parent directory doesn't exist.
|
||||
# Either way I don't know what to do - ask for help
|
||||
openDialog = True
|
||||
|
||||
if os.path.isfile(filename) and not openDialog:
|
||||
if policy == 'Open File Dialog on conflict':
|
||||
if policy == "Open File Dialog on conflict":
|
||||
openDialog = True
|
||||
elif policy == 'Append Unique ID on conflict':
|
||||
elif policy == "Append Unique ID on conflict":
|
||||
fn, ext = os.path.splitext(filename)
|
||||
nr = fn[-3:]
|
||||
n = 1
|
||||
@@ -157,7 +156,9 @@ class CommandPathPost:
|
||||
filename = "%s%03d%s" % (fn, n, ext)
|
||||
|
||||
if openDialog:
|
||||
foo = QtGui.QFileDialog.getSaveFileName(QtGui.QApplication.activeWindow(), "Output File", filename)
|
||||
foo = QtGui.QFileDialog.getSaveFileName(
|
||||
QtGui.QApplication.activeWindow(), "Output File", filename
|
||||
)
|
||||
if foo[0]:
|
||||
filename = foo[0]
|
||||
else:
|
||||
@@ -176,10 +177,12 @@ class CommandPathPost:
|
||||
return dlg.exec_()
|
||||
|
||||
def GetResources(self):
|
||||
return {'Pixmap': 'Path_Post',
|
||||
'MenuText': QtCore.QT_TRANSLATE_NOOP("Path_Post", "Post Process"),
|
||||
'Accel': "P, P",
|
||||
'ToolTip': QtCore.QT_TRANSLATE_NOOP("Path_Post", "Post Process the selected Job")}
|
||||
return {
|
||||
"Pixmap": "Path_Post",
|
||||
"MenuText": QT_TRANSLATE_NOOP("Path_Post", "Post Process"),
|
||||
"Accel": "P, P",
|
||||
"ToolTip": QT_TRANSLATE_NOOP("Path_Post", "Post Process the selected Job"),
|
||||
}
|
||||
|
||||
def IsActive(self):
|
||||
if FreeCAD.ActiveDocument is not None:
|
||||
@@ -198,10 +201,10 @@ class CommandPathPost:
|
||||
if hasattr(job, "PostProcessorArgs") and job.PostProcessorArgs:
|
||||
postArgs = job.PostProcessorArgs
|
||||
elif hasattr(job, "PostProcessor") and job.PostProcessor:
|
||||
postArgs = ''
|
||||
postArgs = ""
|
||||
|
||||
postname = self.resolvePostProcessor(job)
|
||||
filename = '-'
|
||||
filename = "-"
|
||||
if postname and needFilename:
|
||||
filename = self.resolveFileName(job)
|
||||
|
||||
@@ -211,12 +214,11 @@ class CommandPathPost:
|
||||
gcode = processor.export(objs, filename, postArgs)
|
||||
return (False, gcode, filename)
|
||||
else:
|
||||
return (True, '', filename)
|
||||
return (True, "", filename)
|
||||
|
||||
def Activated(self):
|
||||
PathLog.track()
|
||||
FreeCAD.ActiveDocument.openTransaction(
|
||||
translate("Path_Post", "Post Process the Selected path(s)"))
|
||||
FreeCAD.ActiveDocument.openTransaction("Post Process the Selected path(s)")
|
||||
FreeCADGui.addModule("PathScripts.PathPost")
|
||||
|
||||
# Attempt to figure out what the user wants to post-process
|
||||
@@ -227,7 +229,9 @@ class CommandPathPost:
|
||||
|
||||
selected = FreeCADGui.Selection.getSelectionEx()
|
||||
if len(selected) > 1:
|
||||
FreeCAD.Console.PrintError("Please select a single job or other path object\n")
|
||||
FreeCAD.Console.PrintError(
|
||||
"Please select a single job or other path object\n"
|
||||
)
|
||||
return
|
||||
elif len(selected) == 1:
|
||||
sel = selected[0].Object
|
||||
@@ -267,7 +271,7 @@ class CommandPathPost:
|
||||
|
||||
postlist = []
|
||||
|
||||
if orderby == 'Fixture':
|
||||
if orderby == "Fixture":
|
||||
PathLog.debug("Ordering by Fixture")
|
||||
# Order by fixture means all operations and tool changes will be completed in one
|
||||
# fixture before moving to the next.
|
||||
@@ -279,7 +283,13 @@ class CommandPathPost:
|
||||
c1 = Path.Command(f)
|
||||
fobj.Path = Path.Path([c1])
|
||||
if index != 0:
|
||||
c2 = Path.Command("G0 Z" + str(job.Stock.Shape.BoundBox.ZMax + job.SetupSheet.ClearanceHeightOffset.Value))
|
||||
c2 = Path.Command(
|
||||
"G0 Z"
|
||||
+ str(
|
||||
job.Stock.Shape.BoundBox.ZMax
|
||||
+ job.SetupSheet.ClearanceHeightOffset.Value
|
||||
)
|
||||
)
|
||||
fobj.Path.addCommands(c2)
|
||||
fobj.InList.append(job)
|
||||
sublist = [fobj]
|
||||
@@ -287,7 +297,7 @@ class CommandPathPost:
|
||||
# Now generate the gcode
|
||||
for obj in job.Operations.Group:
|
||||
tc = PathUtil.toolControllerForOp(obj)
|
||||
if tc is not None and PathUtil.opProperty(obj, 'Active'):
|
||||
if tc is not None and PathUtil.opProperty(obj, "Active"):
|
||||
if tc.ToolNumber != currTool or split is True:
|
||||
sublist.append(tc)
|
||||
PathLog.debug("Appending TC: {}".format(tc.Name))
|
||||
@@ -295,7 +305,7 @@ class CommandPathPost:
|
||||
sublist.append(obj)
|
||||
postlist.append(sublist)
|
||||
|
||||
elif orderby == 'Tool':
|
||||
elif orderby == "Tool":
|
||||
PathLog.debug("Ordering by Tool")
|
||||
# Order by tool means tool changes are minimized.
|
||||
# all operations with the current tool are processed in the current
|
||||
@@ -307,7 +317,13 @@ class CommandPathPost:
|
||||
# create an object to serve as the fixture path
|
||||
fobj = _TempObject()
|
||||
c1 = Path.Command(f)
|
||||
c2 = Path.Command("G0 Z" + str(job.Stock.Shape.BoundBox.ZMax + job.SetupSheet.ClearanceHeightOffset.Value))
|
||||
c2 = Path.Command(
|
||||
"G0 Z"
|
||||
+ str(
|
||||
job.Stock.Shape.BoundBox.ZMax
|
||||
+ job.SetupSheet.ClearanceHeightOffset.Value
|
||||
)
|
||||
)
|
||||
fobj.Path = Path.Path([c1, c2])
|
||||
fobj.InList.append(job)
|
||||
fixturelist.append(fobj)
|
||||
@@ -319,16 +335,20 @@ class CommandPathPost:
|
||||
for idx, obj in enumerate(job.Operations.Group):
|
||||
|
||||
# check if the operation is active
|
||||
active = PathUtil.opProperty(obj, 'Active')
|
||||
active = PathUtil.opProperty(obj, "Active")
|
||||
|
||||
tc = PathUtil.toolControllerForOp(obj)
|
||||
if tc is None or tc.ToolNumber == currTool and active:
|
||||
curlist.append(obj)
|
||||
elif tc.ToolNumber != currTool and currTool is None and active: # first TC
|
||||
elif (
|
||||
tc.ToolNumber != currTool and currTool is None and active
|
||||
): # first TC
|
||||
sublist.append(tc)
|
||||
curlist.append(obj)
|
||||
currTool = tc.ToolNumber
|
||||
elif tc.ToolNumber != currTool and currTool is not None and active: # TC
|
||||
elif (
|
||||
tc.ToolNumber != currTool and currTool is not None and active
|
||||
): # TC
|
||||
for fixture in fixturelist:
|
||||
sublist.append(fixture)
|
||||
sublist.extend(curlist)
|
||||
@@ -343,7 +363,7 @@ class CommandPathPost:
|
||||
sublist.extend(curlist)
|
||||
postlist.append(sublist)
|
||||
|
||||
elif orderby == 'Operation':
|
||||
elif orderby == "Operation":
|
||||
PathLog.debug("Ordering by Operation")
|
||||
# Order by operation means ops are done in each fixture in
|
||||
# sequence.
|
||||
@@ -352,7 +372,7 @@ class CommandPathPost:
|
||||
|
||||
# Now generate the gcode
|
||||
for obj in job.Operations.Group:
|
||||
if PathUtil.opProperty(obj, 'Active'):
|
||||
if PathUtil.opProperty(obj, "Active"):
|
||||
sublist = []
|
||||
PathLog.debug("obj: {}".format(obj.Name))
|
||||
for f in wcslist:
|
||||
@@ -360,7 +380,13 @@ class CommandPathPost:
|
||||
c1 = Path.Command(f)
|
||||
fobj.Path = Path.Path([c1])
|
||||
if not firstFixture:
|
||||
c2 = Path.Command("G0 Z" + str(job.Stock.Shape.BoundBox.ZMax + job.SetupSheet.ClearanceHeightOffset.Value))
|
||||
c2 = Path.Command(
|
||||
"G0 Z"
|
||||
+ str(
|
||||
job.Stock.Shape.BoundBox.ZMax
|
||||
+ job.SetupSheet.ClearanceHeightOffset.Value
|
||||
)
|
||||
)
|
||||
fobj.Path.addCommands(c2)
|
||||
fobj.InList.append(job)
|
||||
sublist.append(fobj)
|
||||
@@ -374,7 +400,7 @@ class CommandPathPost:
|
||||
postlist.append(sublist)
|
||||
|
||||
fail = True
|
||||
rc = ''
|
||||
rc = ""
|
||||
if split:
|
||||
for slist in postlist:
|
||||
(fail, rc, filename) = self.exportObjectsWith(slist, job)
|
||||
@@ -400,6 +426,6 @@ class CommandPathPost:
|
||||
|
||||
if FreeCAD.GuiUp:
|
||||
# register the FreeCAD command
|
||||
FreeCADGui.addCommand('Path_Post', CommandPathPost())
|
||||
FreeCADGui.addCommand("Path_Post", CommandPathPost())
|
||||
|
||||
FreeCAD.Console.PrintLog("Loading PathPost... done\n")
|
||||
|
||||
@@ -1,34 +1,36 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#***************************************************************************
|
||||
#* Copyright (c) 2014 Yorik van Havre <yorik@uncreated.net> *
|
||||
#* *
|
||||
#* This file is part of the FreeCAD CAx development system. *
|
||||
#* *
|
||||
#* This program is free software; you can redistribute it and/or modify *
|
||||
#* it under the terms of the GNU Lesser General Public License (LGPL) *
|
||||
#* as published by the Free Software Foundation; either version 2 of *
|
||||
#* the License, or (at your option) any later version. *
|
||||
#* for detail see the LICENCE text file. *
|
||||
#* *
|
||||
#* FreeCAD is distributed in the hope that it will be useful, *
|
||||
#* but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||
#* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
||||
#* GNU Lesser General Public License for more details. *
|
||||
#* *
|
||||
#* You should have received a copy of the GNU Library General Public *
|
||||
#* License along with FreeCAD; if not, write to the Free Software *
|
||||
#* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 *
|
||||
#* USA *
|
||||
#* *
|
||||
#***************************************************************************
|
||||
# ***************************************************************************
|
||||
# * Copyright (c) 2014 Yorik van Havre <yorik@uncreated.net> *
|
||||
# * *
|
||||
# * This file is part of the FreeCAD CAx development system. *
|
||||
# * *
|
||||
# * This program is free software; you can redistribute it and/or modify *
|
||||
# * it under the terms of the GNU Lesser General Public License (LGPL) *
|
||||
# * as published by the Free Software Foundation; either version 2 of *
|
||||
# * the License, or (at your option) any later version. *
|
||||
# * for detail see the LICENCE text file. *
|
||||
# * *
|
||||
# * FreeCAD is distributed in the hope that it will be useful, *
|
||||
# * but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||
# * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
||||
# * GNU Lesser General Public License for more details. *
|
||||
# * *
|
||||
# * You should have received a copy of the GNU Library General Public *
|
||||
# * License along with FreeCAD; if not, write to the Free Software *
|
||||
# * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 *
|
||||
# * USA *
|
||||
# * *
|
||||
# ***************************************************************************
|
||||
|
||||
'''
|
||||
"""
|
||||
These are a common functions and classes for creating custom post processors.
|
||||
'''
|
||||
"""
|
||||
|
||||
from PySide import QtCore, QtGui
|
||||
import FreeCAD
|
||||
|
||||
translate = FreeCAD.Qt.translate
|
||||
|
||||
FreeCADGui = None
|
||||
if FreeCAD.GuiUp:
|
||||
import FreeCADGui
|
||||
@@ -41,15 +43,16 @@ class GCodeHighlighter(QtGui.QSyntaxHighlighter):
|
||||
keywordFormat = QtGui.QTextCharFormat()
|
||||
keywordFormat.setForeground(QtCore.Qt.cyan)
|
||||
keywordFormat.setFontWeight(QtGui.QFont.Bold)
|
||||
keywordPatterns = ["\\bG[0-9]+\\b",
|
||||
"\\bM[0-9]+\\b"]
|
||||
keywordPatterns = ["\\bG[0-9]+\\b", "\\bM[0-9]+\\b"]
|
||||
|
||||
self.highlightingRules = [(QtCore.QRegExp(pattern), keywordFormat) for pattern in keywordPatterns]
|
||||
self.highlightingRules = [
|
||||
(QtCore.QRegExp(pattern), keywordFormat) for pattern in keywordPatterns
|
||||
]
|
||||
|
||||
speedFormat = QtGui.QTextCharFormat()
|
||||
speedFormat.setFontWeight(QtGui.QFont.Bold)
|
||||
speedFormat.setForeground(QtCore.Qt.green)
|
||||
self.highlightingRules.append((QtCore.QRegExp("\\bF[0-9\\.]+\\b"),speedFormat))
|
||||
self.highlightingRules.append((QtCore.QRegExp("\\bF[0-9\\.]+\\b"), speedFormat))
|
||||
|
||||
def highlightBlock(self, text):
|
||||
for pattern, hlFormat in self.highlightingRules:
|
||||
@@ -61,12 +64,11 @@ class GCodeHighlighter(QtGui.QSyntaxHighlighter):
|
||||
index = expression.indexIn(text, index + length)
|
||||
|
||||
|
||||
|
||||
class GCodeEditorDialog(QtGui.QDialog):
|
||||
def __init__(self, parent = None):
|
||||
def __init__(self, parent=None):
|
||||
if parent is None:
|
||||
parent = FreeCADGui.getMainWindow()
|
||||
QtGui.QDialog.__init__(self,parent)
|
||||
QtGui.QDialog.__init__(self, parent)
|
||||
|
||||
layout = QtGui.QVBoxLayout(self)
|
||||
|
||||
@@ -83,7 +85,9 @@ class GCodeEditorDialog(QtGui.QDialog):
|
||||
# OK and Cancel buttons
|
||||
self.buttons = QtGui.QDialogButtonBox(
|
||||
QtGui.QDialogButtonBox.Ok | QtGui.QDialogButtonBox.Cancel,
|
||||
QtCore.Qt.Horizontal, self)
|
||||
QtCore.Qt.Horizontal,
|
||||
self,
|
||||
)
|
||||
layout.addWidget(self.buttons)
|
||||
|
||||
# restore placement and size
|
||||
@@ -111,45 +115,68 @@ class GCodeEditorDialog(QtGui.QDialog):
|
||||
|
||||
|
||||
def stringsplit(commandline):
|
||||
returndict = {'command':None, 'X':None, 'Y':None, 'Z':None, 'A':None, 'B':None, 'F':None, 'T':None, 'S':None, 'I':None, 'J':None,'K':None, 'txt': None}
|
||||
returndict = {
|
||||
"command": None,
|
||||
"X": None,
|
||||
"Y": None,
|
||||
"Z": None,
|
||||
"A": None,
|
||||
"B": None,
|
||||
"F": None,
|
||||
"T": None,
|
||||
"S": None,
|
||||
"I": None,
|
||||
"J": None,
|
||||
"K": None,
|
||||
"txt": None,
|
||||
}
|
||||
wordlist = [a.strip() for a in commandline.split(" ")]
|
||||
if wordlist[0][0] == '(':
|
||||
returndict['command'] = 'message'
|
||||
returndict['txt'] = wordlist[0]
|
||||
if wordlist[0][0] == "(":
|
||||
returndict["command"] = "message"
|
||||
returndict["txt"] = wordlist[0]
|
||||
else:
|
||||
returndict['command'] = wordlist[0]
|
||||
returndict["command"] = wordlist[0]
|
||||
for word in wordlist[1:]:
|
||||
returndict[word[0]] = word[1:]
|
||||
|
||||
return returndict
|
||||
|
||||
def fmt(num,dec,units):
|
||||
''' used to format axis moves, feedrate, etc for decimal places and units'''
|
||||
if units == 'G21': #metric
|
||||
fnum = '%.*f' % (dec, num)
|
||||
else: #inch
|
||||
fnum = '%.*f' % (dec, num/25.4) #since FreeCAD uses metric units internally
|
||||
|
||||
def fmt(num, dec, units):
|
||||
"""used to format axis moves, feedrate, etc for decimal places and units"""
|
||||
if units == "G21": # metric
|
||||
fnum = "%.*f" % (dec, num)
|
||||
else: # inch
|
||||
fnum = "%.*f" % (dec, num / 25.4) # since FreeCAD uses metric units internally
|
||||
return fnum
|
||||
|
||||
|
||||
def editor(gcode):
|
||||
'''pops up a handy little editor to look at the code output '''
|
||||
"""pops up a handy little editor to look at the code output"""
|
||||
|
||||
prefs = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/Path")
|
||||
# default Max Highlighter Size = 512 Ko
|
||||
defaultMHS = 512 * 1024
|
||||
mhs = prefs.GetUnsigned('inspecteditorMaxHighlighterSize', defaultMHS)
|
||||
mhs = prefs.GetUnsigned("inspecteditorMaxHighlighterSize", defaultMHS)
|
||||
|
||||
dia = GCodeEditorDialog()
|
||||
dia.editor.setText(gcode)
|
||||
gcodeSize = len(dia.editor.toPlainText())
|
||||
if (gcodeSize <= mhs):
|
||||
if gcodeSize <= mhs:
|
||||
# because of poor performance, syntax highlighting is
|
||||
# limited to mhs octets (default 512 KB).
|
||||
# It seems than the response time curve has an inflexion near 500 KB
|
||||
# beyond 500 KB, the response time increases exponentially.
|
||||
dia.highlighter = GCodeHighlighter(dia.editor.document())
|
||||
else:
|
||||
FreeCAD.Console.PrintMessage(translate("Path", "GCode size too big ({} o), disabling syntax highlighter.".format(gcodeSize)))
|
||||
FreeCAD.Console.PrintMessage(
|
||||
translate(
|
||||
"Path",
|
||||
"GCode size too big ({} o), disabling syntax highlighter.".format(
|
||||
gcodeSize
|
||||
),
|
||||
)
|
||||
)
|
||||
result = dia.exec_()
|
||||
if result: # If user selected 'OK' get modified G Code
|
||||
final = dia.editor.toPlainText()
|
||||
@@ -157,14 +184,12 @@ def editor(gcode):
|
||||
final = gcode
|
||||
return final
|
||||
|
||||
def fcoms(string,commentsym):
|
||||
''' filter and rebuild comments with user preferred comment symbol'''
|
||||
if len(commentsym)==1:
|
||||
s1 = string.replace('(', commentsym)
|
||||
comment = s1.replace(')', '')
|
||||
|
||||
def fcoms(string, commentsym):
|
||||
"""filter and rebuild comments with user preferred comment symbol"""
|
||||
if len(commentsym) == 1:
|
||||
s1 = string.replace("(", commentsym)
|
||||
comment = s1.replace(")", "")
|
||||
else:
|
||||
return string
|
||||
return comment
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -22,7 +22,7 @@
|
||||
# ***************************************************************************/
|
||||
|
||||
|
||||
'''
|
||||
"""
|
||||
This is an example preprocessor file for the Path workbench. Its aim is to
|
||||
open a gcode file, parse its contents, and create the appropriate objects
|
||||
in FreeCAD.
|
||||
@@ -40,7 +40,7 @@ assumed. The user should carefully examine the resulting gcode!
|
||||
|
||||
Read the Path Workbench documentation to know how to create Path objects
|
||||
from GCode.
|
||||
'''
|
||||
"""
|
||||
|
||||
import os
|
||||
import FreeCAD
|
||||
@@ -50,23 +50,23 @@ import re
|
||||
import PathScripts.PathCustom as PathCustom
|
||||
import PathScripts.PathCustomGui as PathCustomGui
|
||||
import PathScripts.PathOpGui as PathOpGui
|
||||
from PySide import QtCore
|
||||
from PySide.QtCore import QT_TRANSLATE_NOOP
|
||||
|
||||
# LEVEL = PathLog.Level.DEBUG
|
||||
LEVEL = PathLog.Level.INFO
|
||||
PathLog.setLevel(LEVEL, PathLog.thisModule())
|
||||
|
||||
if LEVEL == PathLog.Level.DEBUG:
|
||||
if False:
|
||||
PathLog.setLevel(PathLog.Level.DEBUG, PathLog.thisModule())
|
||||
PathLog.trackModule(PathLog.thisModule())
|
||||
else:
|
||||
PathLog.setLevel(PathLog.Level.INFO, PathLog.thisModule())
|
||||
|
||||
|
||||
# to distinguish python built-in open function from the one declared below
|
||||
if open.__module__ in ['__builtin__', 'io']:
|
||||
if open.__module__ in ["__builtin__", "io"]:
|
||||
pythonopen = open
|
||||
|
||||
|
||||
def open(filename):
|
||||
"called when freecad opens a file."
|
||||
"""called when freecad opens a file."""
|
||||
PathLog.track(filename)
|
||||
docname = os.path.splitext(os.path.basename(filename))[0]
|
||||
doc = FreeCAD.newDocument(docname)
|
||||
@@ -83,17 +83,17 @@ def matchToolController(op, toolnumber):
|
||||
|
||||
|
||||
def insert(filename, docname):
|
||||
"called when freecad imports a file"
|
||||
"""called when freecad imports a file"""
|
||||
PathLog.track(filename)
|
||||
gfile = pythonopen(filename)
|
||||
gcode = gfile.read()
|
||||
gfile.close()
|
||||
|
||||
# Regular expression to match tool changes in the format 'M6 Tn'
|
||||
p = re.compile('[mM]+?\s?0?6\s?T\d*\s')
|
||||
p = re.compile("[mM]+?\s?0?6\s?T\d*\s")
|
||||
|
||||
# split the gcode on tool changes
|
||||
paths = re.split('([mM]+?\s?0?6\s?T\d*\s)', gcode)
|
||||
paths = re.split("([mM]+?\s?0?6\s?T\d*\s)", gcode)
|
||||
|
||||
# iterate the gcode sections and add customs for each
|
||||
toolnumber = 0
|
||||
@@ -103,7 +103,7 @@ def insert(filename, docname):
|
||||
# if the section is a tool change, extract the tool number
|
||||
m = p.match(path)
|
||||
if m:
|
||||
toolnumber = int(m.group().split('T')[-1])
|
||||
toolnumber = int(m.group().split("T")[-1])
|
||||
continue
|
||||
|
||||
# Parse the gcode and throw away any empty lists
|
||||
@@ -113,10 +113,15 @@ def insert(filename, docname):
|
||||
|
||||
# Create a custom and viewobject
|
||||
obj = PathCustom.Create("Custom")
|
||||
res = PathOpGui.CommandResources('Custom', PathCustom.Create,
|
||||
PathCustomGui.TaskPanelOpPage,
|
||||
'Path_Custom',
|
||||
QtCore.QT_TRANSLATE_NOOP('Path_Custom', 'Custom'), '', '')
|
||||
res = PathOpGui.CommandResources(
|
||||
"Custom",
|
||||
PathCustom.Create,
|
||||
PathCustomGui.TaskPanelOpPage,
|
||||
"Path_Custom",
|
||||
QT_TRANSLATE_NOOP("Path_Custom", "Custom"),
|
||||
"",
|
||||
"",
|
||||
)
|
||||
obj.ViewObject.Proxy = PathOpGui.ViewProvider(obj.ViewObject, res)
|
||||
obj.ViewObject.Proxy.setDeleteObjectsOnReject(False)
|
||||
|
||||
@@ -127,20 +132,28 @@ def insert(filename, docname):
|
||||
FreeCAD.ActiveDocument.recompute()
|
||||
|
||||
|
||||
|
||||
def parse(inputstring):
|
||||
"parse(inputstring): returns a parsed output string"
|
||||
|
||||
supported = ['G0', 'G00',
|
||||
'G1', 'G01',
|
||||
'G2', 'G02',
|
||||
'G3', 'G03',
|
||||
'G81', 'G82', 'G83',
|
||||
'G90', 'G91']
|
||||
supported = [
|
||||
"G0",
|
||||
"G00",
|
||||
"G1",
|
||||
"G01",
|
||||
"G2",
|
||||
"G02",
|
||||
"G3",
|
||||
"G03",
|
||||
"G81",
|
||||
"G82",
|
||||
"G83",
|
||||
"G90",
|
||||
"G91",
|
||||
]
|
||||
|
||||
axis = ["X", "Y", "Z", "A", "B", "C", "U", "V", "W"]
|
||||
|
||||
print("preprocessing...")
|
||||
FreeCAD.Console.PrintMessage("preprocessing...\n")
|
||||
PathLog.track(inputstring)
|
||||
# split the input by line
|
||||
lines = inputstring.splitlines()
|
||||
@@ -178,7 +191,7 @@ def parse(inputstring):
|
||||
elif currcommand[0] in axis and lastcommand:
|
||||
output.append(lastcommand + " " + lin)
|
||||
|
||||
print("done preprocessing.")
|
||||
FreeCAD.Console.PrintMessage("done preprocessing.\n")
|
||||
return output
|
||||
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -35,7 +35,7 @@ from FreeCAD import Units
|
||||
import PathScripts.PathUtil as PathUtil
|
||||
import PathScripts.PostUtils as PostUtils
|
||||
|
||||
Revised = '2020-11-03' # Revision date for this file.
|
||||
Revised = "2020-11-03" # Revision date for this file.
|
||||
|
||||
# *****************************************************************************
|
||||
# * Due to the fundamentals of the FreeCAD pre-processor, *
|
||||
@@ -47,155 +47,148 @@ Revised = '2020-11-03' # Revision date for this file.
|
||||
# *****************************************************************************
|
||||
|
||||
|
||||
TOOLTIP = '''
|
||||
TOOLTIP = """
|
||||
Generate g-code from a Path that is compatible with the Marlin controller.
|
||||
import marlin_post
|
||||
marlin_post.export(object, "/path/to/file.nc")
|
||||
'''
|
||||
"""
|
||||
|
||||
# *****************************************************************************
|
||||
# * Initial configuration, not changeable *
|
||||
# *****************************************************************************
|
||||
MOTION_MODE = 'G90' # G90 only, for absolute moves
|
||||
WORK_PLANE = 'G17' # G17 only, XY plane, for vertical milling
|
||||
UNITS = 'G21' # G21 only, for metric
|
||||
UNIT_FORMAT = 'mm'
|
||||
UNIT_FEED_FORMAT = 'mm/s'
|
||||
MOTION_MODE = "G90" # G90 only, for absolute moves
|
||||
WORK_PLANE = "G17" # G17 only, XY plane, for vertical milling
|
||||
UNITS = "G21" # G21 only, for metric
|
||||
UNIT_FORMAT = "mm"
|
||||
UNIT_FEED_FORMAT = "mm/s"
|
||||
|
||||
# *****************************************************************************
|
||||
# * Initial configuration, changeable via command line arguments *
|
||||
# *****************************************************************************
|
||||
PRECISION = 3 # Decimal places displayed for metric
|
||||
DRILL_RETRACT_MODE = 'G98' # End of drill-cycle retractation type. G99
|
||||
PRECISION = 3 # Decimal places displayed for metric
|
||||
DRILL_RETRACT_MODE = "G98" # End of drill-cycle retractation type. G99
|
||||
# is the alternative.
|
||||
TRANSLATE_DRILL_CYCLES = True # If true, G81, G82, and G83 are translated
|
||||
TRANSLATE_DRILL_CYCLES = True # If true, G81, G82, and G83 are translated
|
||||
# into G0/G1 moves
|
||||
OUTPUT_TOOL_CHANGE = False # Do not output M6 tool change (comment it)
|
||||
RETURN_TO = None # None = No movement at end of program
|
||||
SPINDLE_WAIT = 3 # 0 == No waiting after M3 / M4
|
||||
MODAL = False # True: Commands are suppressed if they are
|
||||
OUTPUT_TOOL_CHANGE = False # Do not output M6 tool change (comment it)
|
||||
RETURN_TO = None # None = No movement at end of program
|
||||
SPINDLE_WAIT = 3 # 0 == No waiting after M3 / M4
|
||||
MODAL = False # True: Commands are suppressed if they are
|
||||
# the same as the previous line
|
||||
LINENR = 100 # Line number starting value
|
||||
LINEINCR = 10 # Line number increment
|
||||
PRE_OPERATION = '''''' # Pre operation text will be inserted before
|
||||
LINENR = 100 # Line number starting value
|
||||
LINEINCR = 10 # Line number increment
|
||||
PRE_OPERATION = """""" # Pre operation text will be inserted before
|
||||
# every operation
|
||||
POST_OPERATION = '''''' # Post operation text will be inserted after
|
||||
POST_OPERATION = """""" # Post operation text will be inserted after
|
||||
# every operation
|
||||
TOOL_CHANGE = '''''' # Tool Change commands will be inserted
|
||||
TOOL_CHANGE = """""" # Tool Change commands will be inserted
|
||||
# before a tool change
|
||||
|
||||
# *****************************************************************************
|
||||
# * Initial gcode output options, changeable via command line arguments *
|
||||
# *****************************************************************************
|
||||
OUTPUT_HEADER = True # Output header in output gcode file
|
||||
OUTPUT_COMMENTS = True # Comments in output gcode file
|
||||
OUTPUT_FINISH = False # Include an operation finished comment
|
||||
OUTPUT_PATH = False # Include a Path: comment
|
||||
OUTPUT_MARLIN_CONFIG = False # Display expected #defines for Marlin config
|
||||
OUTPUT_LINE_NUMBERS = False # Output line numbers in output gcode file
|
||||
OUTPUT_BCNC = False # Add bCNC operation block headers in output
|
||||
OUTPUT_HEADER = True # Output header in output gcode file
|
||||
OUTPUT_COMMENTS = True # Comments in output gcode file
|
||||
OUTPUT_FINISH = False # Include an operation finished comment
|
||||
OUTPUT_PATH = False # Include a Path: comment
|
||||
OUTPUT_MARLIN_CONFIG = False # Display expected #defines for Marlin config
|
||||
OUTPUT_LINE_NUMBERS = False # Output line numbers in output gcode file
|
||||
OUTPUT_BCNC = False # Add bCNC operation block headers in output
|
||||
# gcode file
|
||||
SHOW_EDITOR = True # Display the resulting gcode file
|
||||
SHOW_EDITOR = True # Display the resulting gcode file
|
||||
|
||||
# *****************************************************************************
|
||||
# * Command line arguments *
|
||||
# *****************************************************************************
|
||||
parser = argparse.ArgumentParser(prog='marlin', add_help=False)
|
||||
parser = argparse.ArgumentParser(prog="marlin", add_help=False)
|
||||
parser.add_argument("--header", action="store_true", help="output headers (default)")
|
||||
parser.add_argument("--no-header", action="store_true", help="suppress header output")
|
||||
parser.add_argument("--comments", action="store_true", help="output comment (default)")
|
||||
parser.add_argument(
|
||||
'--header',
|
||||
action='store_true',
|
||||
help='output headers (default)')
|
||||
"--no-comments", action="store_true", help="suppress comment output"
|
||||
)
|
||||
parser.add_argument(
|
||||
'--no-header',
|
||||
action='store_true',
|
||||
help='suppress header output')
|
||||
"--finish-comments", action="store_true", help="output finish-comment"
|
||||
)
|
||||
parser.add_argument(
|
||||
'--comments',
|
||||
action='store_true',
|
||||
help='output comment (default)')
|
||||
"--no-finish-comments",
|
||||
action="store_true",
|
||||
help="suppress finish-comment output (default)",
|
||||
)
|
||||
parser.add_argument("--path-comments", action="store_true", help="output path-comment")
|
||||
parser.add_argument(
|
||||
'--no-comments',
|
||||
action='store_true',
|
||||
help='suppress comment output')
|
||||
"--no-path-comments",
|
||||
action="store_true",
|
||||
help="suppress path-comment output (default)",
|
||||
)
|
||||
parser.add_argument(
|
||||
'--finish-comments',
|
||||
action='store_true',
|
||||
help='output finish-comment')
|
||||
"--marlin-config", action="store_true", help="output #defines for Marlin"
|
||||
)
|
||||
parser.add_argument(
|
||||
'--no-finish-comments',
|
||||
action='store_true',
|
||||
help='suppress finish-comment output (default)')
|
||||
"--no-marlin-config",
|
||||
action="store_true",
|
||||
help="suppress output #defines for Marlin (default)",
|
||||
)
|
||||
parser.add_argument(
|
||||
'--path-comments',
|
||||
action='store_true',
|
||||
help='output path-comment')
|
||||
"--line-numbers", action="store_true", help="prefix with line numbers"
|
||||
)
|
||||
parser.add_argument(
|
||||
'--no-path-comments',
|
||||
action='store_true',
|
||||
help='suppress path-comment output (default)')
|
||||
"--no-line-numbers",
|
||||
action="store_true",
|
||||
help="do not prefix with line numbers (default)",
|
||||
)
|
||||
parser.add_argument(
|
||||
'--marlin-config',
|
||||
action='store_true',
|
||||
help='output #defines for Marlin')
|
||||
"--show-editor",
|
||||
action="store_true",
|
||||
help="pop up editor before writing output (default)",
|
||||
)
|
||||
parser.add_argument(
|
||||
'--no-marlin-config',
|
||||
action='store_true',
|
||||
help='suppress output #defines for Marlin (default)')
|
||||
"--no-show-editor",
|
||||
action="store_true",
|
||||
help="do not pop up editor before writing output",
|
||||
)
|
||||
parser.add_argument(
|
||||
'--line-numbers',
|
||||
action='store_true',
|
||||
help='prefix with line numbers')
|
||||
"--precision", default="3", help="number of digits of precision, default=3"
|
||||
)
|
||||
parser.add_argument(
|
||||
'--no-line-numbers',
|
||||
action='store_true',
|
||||
help='do not prefix with line numbers (default)')
|
||||
"--translate_drill",
|
||||
action="store_true",
|
||||
help="translate drill cycles G81, G82, G83 into G0/G1 movements (default)",
|
||||
)
|
||||
parser.add_argument(
|
||||
'--show-editor',
|
||||
action='store_true',
|
||||
help='pop up editor before writing output (default)')
|
||||
"--no-translate_drill",
|
||||
action="store_true",
|
||||
help="do not translate drill cycles G81, G82, G83 into G0/G1 movements",
|
||||
)
|
||||
parser.add_argument(
|
||||
'--no-show-editor',
|
||||
action='store_true',
|
||||
help='do not pop up editor before writing output')
|
||||
"--preamble", help='set commands to be issued before the first command, default=""'
|
||||
)
|
||||
parser.add_argument(
|
||||
'--precision',
|
||||
default='3',
|
||||
help='number of digits of precision, default=3')
|
||||
"--postamble", help='set commands to be issued after the last command, default="M5"'
|
||||
)
|
||||
parser.add_argument(
|
||||
'--translate_drill',
|
||||
action='store_true',
|
||||
help='translate drill cycles G81, G82, G83 into G0/G1 movements (default)')
|
||||
"--tool-change", action="store_true", help="Insert M6 for all tool changes"
|
||||
)
|
||||
parser.add_argument(
|
||||
'--no-translate_drill',
|
||||
action='store_true',
|
||||
help='do not translate drill cycles G81, G82, G83 into G0/G1 movements')
|
||||
parser.add_argument(
|
||||
'--preamble',
|
||||
help='set commands to be issued before the first command, default=""')
|
||||
parser.add_argument(
|
||||
'--postamble',
|
||||
help='set commands to be issued after the last command, default="M5"')
|
||||
parser.add_argument(
|
||||
'--tool-change', action='store_true',
|
||||
help='Insert M6 for all tool changes')
|
||||
parser.add_argument(
|
||||
'--wait-for-spindle',
|
||||
"--wait-for-spindle",
|
||||
type=int,
|
||||
default=3,
|
||||
help='Wait for spindle to reach desired speed after M3 or M4, default=0')
|
||||
help="Wait for spindle to reach desired speed after M3 or M4, default=0",
|
||||
)
|
||||
parser.add_argument(
|
||||
'--return-to',
|
||||
default='',
|
||||
help='When done, move to, e.g. --return-to="3.175, 4.702, 50.915"')
|
||||
"--return-to",
|
||||
default="",
|
||||
help='When done, move to, e.g. --return-to="3.175, 4.702, 50.915"',
|
||||
)
|
||||
parser.add_argument(
|
||||
'--bcnc',
|
||||
action='store_true',
|
||||
help='Add Job operations as bCNC block headers. \
|
||||
Consider suppressing existing comments: Add argument --no-comments')
|
||||
"--bcnc",
|
||||
action="store_true",
|
||||
help="Add Job operations as bCNC block headers. \
|
||||
Consider suppressing existing comments: Add argument --no-comments",
|
||||
)
|
||||
parser.add_argument(
|
||||
'--no-bcnc',
|
||||
action='store_true',
|
||||
help='suppress bCNC block header output (default)')
|
||||
"--no-bcnc", action="store_true", help="suppress bCNC block header output (default)"
|
||||
)
|
||||
TOOLTIP_ARGS = parser.format_help()
|
||||
|
||||
# *****************************************************************************
|
||||
@@ -209,19 +202,19 @@ TOOLTIP_ARGS = parser.format_help()
|
||||
# *****************************************************************************
|
||||
|
||||
# Default preamble text will appear at the beginning of the gcode output file.
|
||||
PREAMBLE = ''''''
|
||||
PREAMBLE = """"""
|
||||
|
||||
# Default postamble text will appear following the last operation.
|
||||
POSTAMBLE = '''M5
|
||||
'''
|
||||
POSTAMBLE = """M5
|
||||
"""
|
||||
|
||||
# *****************************************************************************
|
||||
# * Internal global variables *
|
||||
# *****************************************************************************
|
||||
MOTION_COMMANDS = ['G0', 'G00', 'G1', 'G01', 'G2', 'G02', 'G3', 'G03']
|
||||
RAPID_MOVES = ['G0', 'G00'] # Rapid moves gcode commands definition
|
||||
SUPPRESS_COMMANDS = [''] # These commands are ignored by commenting them out
|
||||
COMMAND_SPACE = ' '
|
||||
MOTION_COMMANDS = ["G0", "G00", "G1", "G01", "G2", "G02", "G3", "G03"]
|
||||
RAPID_MOVES = ["G0", "G00"] # Rapid moves gcode commands definition
|
||||
SUPPRESS_COMMANDS = [""] # These commands are ignored by commenting them out
|
||||
COMMAND_SPACE = " "
|
||||
# Global variables storing current position (Use None for safety.)
|
||||
CURRENT_X = None
|
||||
CURRENT_Y = None
|
||||
@@ -290,9 +283,9 @@ def processArguments(argstring):
|
||||
OUTPUT_TOOL_CHANGE = True
|
||||
if args.return_to:
|
||||
RETURN_TO = args.return_to
|
||||
if RETURN_TO.find(',') == -1:
|
||||
if RETURN_TO.find(",") == -1:
|
||||
RETURN_TO = None
|
||||
print('--return-to coordinates must be specified as:')
|
||||
print("--return-to coordinates must be specified as:")
|
||||
print('--return-to "x.n,y.n,z.n"')
|
||||
if args.bcnc:
|
||||
OUTPUT_BCNC = True
|
||||
@@ -311,14 +304,14 @@ def processArguments(argstring):
|
||||
def dump(obj):
|
||||
for attr in dir(obj):
|
||||
try:
|
||||
if attr.startswith('__'):
|
||||
if attr.startswith("__"):
|
||||
continue
|
||||
print('>' + attr + '<')
|
||||
print(">" + attr + "<")
|
||||
attr_text = "%s = %s" % (attr, getattr(obj, attr))
|
||||
if attr in ['HorizFeed', 'VertFeed']:
|
||||
print('==============\n', attr_text)
|
||||
if 'mm/s' in attr_text:
|
||||
print('===> metric values <===')
|
||||
if attr in ["HorizFeed", "VertFeed"]:
|
||||
print("==============\n", attr_text)
|
||||
if "mm/s" in attr_text:
|
||||
print("===> metric values <===")
|
||||
except Exception: # Insignificant errors
|
||||
# print('==>', obj, attr)
|
||||
pass
|
||||
@@ -334,128 +327,134 @@ def export(objectslist, filename, argstring):
|
||||
global MOTION_MODE
|
||||
global SUPPRESS_COMMANDS
|
||||
|
||||
print('Post Processor: ' + __name__ + ' postprocessing...')
|
||||
gcode = ''
|
||||
print("Post Processor: " + __name__ + " postprocessing...")
|
||||
gcode = ""
|
||||
|
||||
# Write header:
|
||||
if OUTPUT_HEADER:
|
||||
gcode += linenumber() + '(Exported by FreeCAD)\n'
|
||||
gcode += linenumber() + '(Post Processor: ' + __name__
|
||||
gcode += '.py, version: ' + Revised + ')\n'
|
||||
gcode += linenumber() + '(Output Time:' + str(datetime.now()) + ')\n'
|
||||
gcode += linenumber() + "(Exported by FreeCAD)\n"
|
||||
gcode += linenumber() + "(Post Processor: " + __name__
|
||||
gcode += ".py, version: " + Revised + ")\n"
|
||||
gcode += linenumber() + "(Output Time:" + str(datetime.now()) + ")\n"
|
||||
|
||||
# Suppress drill-cycle commands:
|
||||
if TRANSLATE_DRILL_CYCLES:
|
||||
SUPPRESS_COMMANDS += ['G80', 'G98', 'G99']
|
||||
SUPPRESS_COMMANDS += ["G80", "G98", "G99"]
|
||||
|
||||
# Write the preamble:
|
||||
if OUTPUT_COMMENTS:
|
||||
gcode += linenumber() + '(Begin preamble)\n'
|
||||
gcode += linenumber() + "(Begin preamble)\n"
|
||||
for line in PREAMBLE.splitlines(True):
|
||||
gcode += linenumber() + line
|
||||
|
||||
# Write these settings AFTER the preamble,
|
||||
# to prevent the preamble from changing these:
|
||||
if OUTPUT_COMMENTS:
|
||||
gcode += linenumber() + '(Default Configuration)\n'
|
||||
gcode += linenumber() + MOTION_MODE + '\n'
|
||||
gcode += linenumber() + UNITS + '\n'
|
||||
gcode += linenumber() + WORK_PLANE + '\n'
|
||||
gcode += linenumber() + "(Default Configuration)\n"
|
||||
gcode += linenumber() + MOTION_MODE + "\n"
|
||||
gcode += linenumber() + UNITS + "\n"
|
||||
gcode += linenumber() + WORK_PLANE + "\n"
|
||||
|
||||
for obj in objectslist:
|
||||
# Debug...
|
||||
# print('\n' + '*'*70 + '\n')
|
||||
# dump(obj)
|
||||
# print('\n' + '*'*70 + '\n')
|
||||
if not hasattr(obj, 'Path'):
|
||||
print('The object ' + obj.Name +
|
||||
' is not a path. Please select only path and Compounds.')
|
||||
if not hasattr(obj, "Path"):
|
||||
print(
|
||||
"The object "
|
||||
+ obj.Name
|
||||
+ " is not a path. Please select only path and Compounds."
|
||||
)
|
||||
return
|
||||
|
||||
# Skip inactive operations:
|
||||
if PathUtil.opProperty(obj, 'Active') is False:
|
||||
if PathUtil.opProperty(obj, "Active") is False:
|
||||
continue
|
||||
|
||||
# Do the pre_op:
|
||||
if OUTPUT_BCNC:
|
||||
gcode += linenumber() + '(Block-name: ' + obj.Label + ')\n'
|
||||
gcode += linenumber() + '(Block-expand: 0)\n'
|
||||
gcode += linenumber() + '(Block-enable: 1)\n'
|
||||
gcode += linenumber() + "(Block-name: " + obj.Label + ")\n"
|
||||
gcode += linenumber() + "(Block-expand: 0)\n"
|
||||
gcode += linenumber() + "(Block-enable: 1)\n"
|
||||
if OUTPUT_COMMENTS:
|
||||
gcode += linenumber() + '(Begin operation: ' + obj.Label + ')\n'
|
||||
gcode += linenumber() + "(Begin operation: " + obj.Label + ")\n"
|
||||
for line in PRE_OPERATION.splitlines(True):
|
||||
gcode += linenumber() + line
|
||||
|
||||
# Get coolant mode:
|
||||
coolantMode = 'None' # None is the word returned from the operation
|
||||
if hasattr(obj, 'CoolantMode') or hasattr(obj, 'Base') and \
|
||||
hasattr(obj.Base, 'CoolantMode'):
|
||||
if hasattr(obj, 'CoolantMode'):
|
||||
coolantMode = "None" # None is the word returned from the operation
|
||||
if (
|
||||
hasattr(obj, "CoolantMode")
|
||||
or hasattr(obj, "Base")
|
||||
and hasattr(obj.Base, "CoolantMode")
|
||||
):
|
||||
if hasattr(obj, "CoolantMode"):
|
||||
coolantMode = obj.CoolantMode
|
||||
else:
|
||||
coolantMode = obj.Base.CoolantMode
|
||||
|
||||
# Turn coolant on if required:
|
||||
if OUTPUT_COMMENTS:
|
||||
if not coolantMode == 'None':
|
||||
gcode += linenumber() + '(Coolant On:' + coolantMode + ')\n'
|
||||
if coolantMode == 'Flood':
|
||||
gcode += linenumber() + 'M8\n'
|
||||
if coolantMode == 'Mist':
|
||||
gcode += linenumber() + 'M7\n'
|
||||
if not coolantMode == "None":
|
||||
gcode += linenumber() + "(Coolant On:" + coolantMode + ")\n"
|
||||
if coolantMode == "Flood":
|
||||
gcode += linenumber() + "M8\n"
|
||||
if coolantMode == "Mist":
|
||||
gcode += linenumber() + "M7\n"
|
||||
|
||||
# Parse the op:
|
||||
gcode += parse(obj)
|
||||
|
||||
# Do the post_op:
|
||||
if OUTPUT_COMMENTS and OUTPUT_FINISH:
|
||||
gcode += linenumber() + '(Finish operation: ' + obj.Label + ')\n'
|
||||
gcode += linenumber() + "(Finish operation: " + obj.Label + ")\n"
|
||||
for line in POST_OPERATION.splitlines(True):
|
||||
gcode += linenumber() + line
|
||||
|
||||
# Turn coolant off if previously enabled:
|
||||
if not coolantMode == 'None':
|
||||
if not coolantMode == "None":
|
||||
if OUTPUT_COMMENTS:
|
||||
gcode += linenumber() + '(Coolant Off:' + coolantMode + ')\n'
|
||||
gcode += linenumber() + 'M9\n'
|
||||
gcode += linenumber() + "(Coolant Off:" + coolantMode + ")\n"
|
||||
gcode += linenumber() + "M9\n"
|
||||
|
||||
# Do the post_amble:
|
||||
if OUTPUT_BCNC:
|
||||
gcode += linenumber() + '(Block-name: post_amble)\n'
|
||||
gcode += linenumber() + '(Block-expand: 0)\n'
|
||||
gcode += linenumber() + '(Block-enable: 1)\n'
|
||||
gcode += linenumber() + "(Block-name: post_amble)\n"
|
||||
gcode += linenumber() + "(Block-expand: 0)\n"
|
||||
gcode += linenumber() + "(Block-enable: 1)\n"
|
||||
if OUTPUT_COMMENTS:
|
||||
gcode += linenumber() + '(Begin postamble)\n'
|
||||
gcode += linenumber() + "(Begin postamble)\n"
|
||||
for line in POSTAMBLE.splitlines(True):
|
||||
gcode += linenumber() + line
|
||||
|
||||
# Optionally add a final XYZ position to the end of the gcode:
|
||||
if RETURN_TO:
|
||||
first_comma = RETURN_TO.find(',')
|
||||
last_comma = RETURN_TO.rfind(',') # == first_comma if only one comma
|
||||
ref_X = ' X' + RETURN_TO[0: first_comma].strip()
|
||||
first_comma = RETURN_TO.find(",")
|
||||
last_comma = RETURN_TO.rfind(",") # == first_comma if only one comma
|
||||
ref_X = " X" + RETURN_TO[0:first_comma].strip()
|
||||
|
||||
# Z is optional:
|
||||
if last_comma != first_comma:
|
||||
ref_Z = ' Z' + RETURN_TO[last_comma + 1:].strip()
|
||||
ref_Y = ' Y' + RETURN_TO[first_comma + 1:last_comma].strip()
|
||||
ref_Z = " Z" + RETURN_TO[last_comma + 1 :].strip()
|
||||
ref_Y = " Y" + RETURN_TO[first_comma + 1 : last_comma].strip()
|
||||
else:
|
||||
ref_Z = ''
|
||||
ref_Y = ' Y' + RETURN_TO[first_comma + 1:].strip()
|
||||
ref_Z = ""
|
||||
ref_Y = " Y" + RETURN_TO[first_comma + 1 :].strip()
|
||||
|
||||
gcode += linenumber() + 'G0' + ref_X + ref_Y + ref_Z + '\n'
|
||||
gcode += linenumber() + "G0" + ref_X + ref_Y + ref_Z + "\n"
|
||||
|
||||
# Optionally add recommended Marlin 2.x configuration to gcode file:
|
||||
if OUTPUT_MARLIN_CONFIG:
|
||||
gcode += linenumber() + '(Marlin 2.x Configuration)\n'
|
||||
gcode += linenumber() + '(The following should be enabled in)\n'
|
||||
gcode += linenumber() + '(the configuration files of Marlin 2.x)\n'
|
||||
gcode += linenumber() + '(#define ARC_SUPPORT)\n'
|
||||
gcode += linenumber() + '(#define CNC_COORDINATE_SYSTEMS)\n'
|
||||
gcode += linenumber() + '(#define PAREN_COMMENTS)\n'
|
||||
gcode += linenumber() + '(#define GCODE_MOTION_MODES)\n'
|
||||
gcode += linenumber() + '(#define G0_FEEDRATE)\n'
|
||||
gcode += linenumber() + '(define VARIABLE_G0_FEEDRATE)\n'
|
||||
gcode += linenumber() + "(Marlin 2.x Configuration)\n"
|
||||
gcode += linenumber() + "(The following should be enabled in)\n"
|
||||
gcode += linenumber() + "(the configuration files of Marlin 2.x)\n"
|
||||
gcode += linenumber() + "(#define ARC_SUPPORT)\n"
|
||||
gcode += linenumber() + "(#define CNC_COORDINATE_SYSTEMS)\n"
|
||||
gcode += linenumber() + "(#define PAREN_COMMENTS)\n"
|
||||
gcode += linenumber() + "(#define GCODE_MOTION_MODES)\n"
|
||||
gcode += linenumber() + "(#define G0_FEEDRATE)\n"
|
||||
gcode += linenumber() + "(define VARIABLE_G0_FEEDRATE)\n"
|
||||
|
||||
# Show the gcode result dialog:
|
||||
if FreeCAD.GuiUp and SHOW_EDITOR:
|
||||
@@ -469,26 +468,26 @@ def export(objectslist, filename, argstring):
|
||||
else:
|
||||
final = gcode
|
||||
|
||||
print('Done postprocessing.')
|
||||
print("Done postprocessing.")
|
||||
|
||||
# Write the file:
|
||||
with open(filename, 'w') as fp:
|
||||
with open(filename, "w") as fp:
|
||||
fp.write(final)
|
||||
|
||||
|
||||
def linenumber():
|
||||
if not OUTPUT_LINE_NUMBERS:
|
||||
return ''
|
||||
return ""
|
||||
global LINENR
|
||||
global LINEINCR
|
||||
LINENR += LINEINCR
|
||||
return 'N' + str(LINENR) + ' '
|
||||
return "N" + str(LINENR) + " "
|
||||
|
||||
|
||||
def format_outlist(strTable):
|
||||
# construct the line for the final output
|
||||
global COMMAND_SPACE
|
||||
s = ''
|
||||
s = ""
|
||||
for w in strTable:
|
||||
s += w + COMMAND_SPACE
|
||||
return s.strip()
|
||||
@@ -501,27 +500,46 @@ def parse(pathobj):
|
||||
global CURRENT_Y
|
||||
global CURRENT_Z
|
||||
|
||||
out = ''
|
||||
out = ""
|
||||
lastcommand = None
|
||||
precision_string = '.' + str(PRECISION) + 'f'
|
||||
precision_string = "." + str(PRECISION) + "f"
|
||||
|
||||
params = ['X', 'Y', 'Z', 'A', 'B', 'C', 'U', 'V', 'W', 'I', 'J', 'K', 'F',
|
||||
'S', 'T', 'Q', 'R', 'L', 'P']
|
||||
params = [
|
||||
"X",
|
||||
"Y",
|
||||
"Z",
|
||||
"A",
|
||||
"B",
|
||||
"C",
|
||||
"U",
|
||||
"V",
|
||||
"W",
|
||||
"I",
|
||||
"J",
|
||||
"K",
|
||||
"F",
|
||||
"S",
|
||||
"T",
|
||||
"Q",
|
||||
"R",
|
||||
"L",
|
||||
"P",
|
||||
]
|
||||
|
||||
if hasattr(pathobj, 'Group'): # We have a compound or project.
|
||||
if hasattr(pathobj, "Group"): # We have a compound or project.
|
||||
if OUTPUT_COMMENTS:
|
||||
out += linenumber() + '(Compound: ' + pathobj.Label + ')\n'
|
||||
out += linenumber() + "(Compound: " + pathobj.Label + ")\n"
|
||||
for p in pathobj.Group:
|
||||
out += parse(p)
|
||||
return out
|
||||
|
||||
else: # Parsing simple path
|
||||
# groups might contain non-path things like stock.
|
||||
if not hasattr(pathobj, 'Path'):
|
||||
if not hasattr(pathobj, "Path"):
|
||||
return out
|
||||
|
||||
if OUTPUT_COMMENTS and OUTPUT_PATH:
|
||||
out += linenumber() + '(Path: ' + pathobj.Label + ')\n'
|
||||
out += linenumber() + "(Path: " + pathobj.Label + ")\n"
|
||||
|
||||
for c in pathobj.Path.Commands:
|
||||
outlist = []
|
||||
@@ -538,96 +556,102 @@ def parse(pathobj):
|
||||
# Add the remaining parameters in order:
|
||||
for param in params:
|
||||
if param in c.Parameters:
|
||||
if param == 'F':
|
||||
if param == "F":
|
||||
if command not in RAPID_MOVES:
|
||||
feedRate = Units.Quantity(
|
||||
c.Parameters['F'], FreeCAD.Units.Velocity)
|
||||
c.Parameters["F"], FreeCAD.Units.Velocity
|
||||
)
|
||||
if feedRate.getValueAs(UNIT_FEED_FORMAT) > 0.0:
|
||||
outlist.append(param + format(float(
|
||||
feedRate.getValueAs(UNIT_FEED_FORMAT)),
|
||||
precision_string))
|
||||
elif param in ['T', 'H', 'D', 'S', 'P', 'L']:
|
||||
outlist.append(
|
||||
param
|
||||
+ format(
|
||||
float(feedRate.getValueAs(UNIT_FEED_FORMAT)),
|
||||
precision_string,
|
||||
)
|
||||
)
|
||||
elif param in ["T", "H", "D", "S", "P", "L"]:
|
||||
outlist.append(param + str(c.Parameters[param]))
|
||||
elif param in ['A', 'B', 'C']:
|
||||
outlist.append(param + format(
|
||||
c.Parameters[param], precision_string))
|
||||
elif param in ["A", "B", "C"]:
|
||||
outlist.append(
|
||||
param + format(c.Parameters[param], precision_string)
|
||||
)
|
||||
# [X, Y, Z, U, V, W, I, J, K, R, Q]
|
||||
else:
|
||||
pos = Units.Quantity(
|
||||
c.Parameters[param], FreeCAD.Units.Length)
|
||||
outlist.append(param + format(float(
|
||||
pos.getValueAs(UNIT_FORMAT)), precision_string))
|
||||
pos = Units.Quantity(c.Parameters[param], FreeCAD.Units.Length)
|
||||
outlist.append(
|
||||
param
|
||||
+ format(
|
||||
float(pos.getValueAs(UNIT_FORMAT)), precision_string
|
||||
)
|
||||
)
|
||||
|
||||
# Store the latest command:
|
||||
lastcommand = command
|
||||
|
||||
# Capture the current position for subsequent calculations:
|
||||
if command in MOTION_COMMANDS:
|
||||
if 'X' in c.Parameters:
|
||||
CURRENT_X = Units.Quantity(
|
||||
c.Parameters['X'], FreeCAD.Units.Length)
|
||||
if 'Y' in c.Parameters:
|
||||
CURRENT_Y = Units.Quantity(
|
||||
c.Parameters['Y'], FreeCAD.Units.Length)
|
||||
if 'Z' in c.Parameters:
|
||||
CURRENT_Z = Units.Quantity(
|
||||
c.Parameters['Z'], FreeCAD.Units.Length)
|
||||
if "X" in c.Parameters:
|
||||
CURRENT_X = Units.Quantity(c.Parameters["X"], FreeCAD.Units.Length)
|
||||
if "Y" in c.Parameters:
|
||||
CURRENT_Y = Units.Quantity(c.Parameters["Y"], FreeCAD.Units.Length)
|
||||
if "Z" in c.Parameters:
|
||||
CURRENT_Z = Units.Quantity(c.Parameters["Z"], FreeCAD.Units.Length)
|
||||
|
||||
if command in ('G98', 'G99'):
|
||||
if command in ("G98", "G99"):
|
||||
DRILL_RETRACT_MODE = command
|
||||
|
||||
if TRANSLATE_DRILL_CYCLES:
|
||||
if command in ('G81', 'G82', 'G83'):
|
||||
if command in ("G81", "G82", "G83"):
|
||||
out += drill_translate(outlist, command, c.Parameters)
|
||||
# Erase the line just translated:
|
||||
outlist = []
|
||||
|
||||
if SPINDLE_WAIT > 0:
|
||||
if command in ('M3', 'M03', 'M4', 'M04'):
|
||||
out += linenumber() + format_outlist(outlist) + '\n'
|
||||
if command in ("M3", "M03", "M4", "M04"):
|
||||
out += linenumber() + format_outlist(outlist) + "\n"
|
||||
# Marlin: P for milliseconds, S for seconds, change P to S
|
||||
out += linenumber()
|
||||
out += format_outlist(['G4', 'S%s' % SPINDLE_WAIT])
|
||||
out += '\n'
|
||||
out += format_outlist(["G4", "S%s" % SPINDLE_WAIT])
|
||||
out += "\n"
|
||||
outlist = []
|
||||
|
||||
# Check for Tool Change:
|
||||
if command in ('M6', 'M06'):
|
||||
if command in ("M6", "M06"):
|
||||
if OUTPUT_COMMENTS:
|
||||
out += linenumber() + '(Begin toolchange)\n'
|
||||
out += linenumber() + "(Begin toolchange)\n"
|
||||
if OUTPUT_TOOL_CHANGE:
|
||||
for line in TOOL_CHANGE.splitlines(True):
|
||||
out += linenumber() + line
|
||||
if not OUTPUT_TOOL_CHANGE and OUTPUT_COMMENTS:
|
||||
outlist[0] = '(' + outlist[0]
|
||||
outlist[-1] = outlist[-1] + ')'
|
||||
outlist[0] = "(" + outlist[0]
|
||||
outlist[-1] = outlist[-1] + ")"
|
||||
if not OUTPUT_TOOL_CHANGE and not OUTPUT_COMMENTS:
|
||||
outlist = []
|
||||
|
||||
if command == 'message':
|
||||
if command == "message":
|
||||
if OUTPUT_COMMENTS:
|
||||
outlist.pop(0) # remove the command
|
||||
else:
|
||||
out = []
|
||||
|
||||
if command in SUPPRESS_COMMANDS:
|
||||
outlist[0] = '(' + outlist[0]
|
||||
outlist[-1] = outlist[-1] + ')'
|
||||
outlist[0] = "(" + outlist[0]
|
||||
outlist[-1] = outlist[-1] + ")"
|
||||
|
||||
# Remove embedded comments:
|
||||
if not OUTPUT_COMMENTS:
|
||||
tmplist = []
|
||||
list_index = 0
|
||||
while list_index < len(outlist):
|
||||
left_index = outlist[list_index].find('(')
|
||||
left_index = outlist[list_index].find("(")
|
||||
if left_index == -1: # Not a comment
|
||||
tmplist.append(outlist[list_index])
|
||||
else: # This line contains a comment, and possibly more
|
||||
right_index = outlist[list_index].find(')')
|
||||
comment_area = outlist[list_index][
|
||||
left_index: right_index + 1]
|
||||
line_minus_comment = outlist[list_index].replace(
|
||||
comment_area, '').strip()
|
||||
right_index = outlist[list_index].find(")")
|
||||
comment_area = outlist[list_index][left_index : right_index + 1]
|
||||
line_minus_comment = (
|
||||
outlist[list_index].replace(comment_area, "").strip()
|
||||
)
|
||||
if line_minus_comment:
|
||||
# Line contained more than just a comment
|
||||
tmplist.append(line_minus_comment)
|
||||
@@ -637,7 +661,7 @@ def parse(pathobj):
|
||||
|
||||
# Prepend a line number and append a newline
|
||||
if len(outlist) >= 1:
|
||||
out += linenumber() + format_outlist(outlist) + '\n'
|
||||
out += linenumber() + format_outlist(outlist) + "\n"
|
||||
|
||||
return out
|
||||
|
||||
@@ -658,61 +682,56 @@ def drill_translate(outlist, cmd, params):
|
||||
global UNIT_FEED_FORMAT
|
||||
|
||||
class Drill: # Using a class is necessary for the nested functions.
|
||||
gcode = ''
|
||||
gcode = ""
|
||||
|
||||
strFormat = '.' + str(PRECISION) + 'f'
|
||||
strFormat = "." + str(PRECISION) + "f"
|
||||
|
||||
if OUTPUT_COMMENTS: # Comment the original command
|
||||
outlist[0] = '(' + outlist[0]
|
||||
outlist[-1] = outlist[-1] + ')'
|
||||
Drill.gcode += linenumber() + format_outlist(outlist) + '\n'
|
||||
outlist[0] = "(" + outlist[0]
|
||||
outlist[-1] = outlist[-1] + ")"
|
||||
Drill.gcode += linenumber() + format_outlist(outlist) + "\n"
|
||||
|
||||
# Cycle conversion only converts the cycles in the XY plane (G17).
|
||||
# --> ZX (G18) and YZ (G19) planes produce false gcode.
|
||||
drill_X = Units.Quantity(params['X'], FreeCAD.Units.Length)
|
||||
drill_Y = Units.Quantity(params['Y'], FreeCAD.Units.Length)
|
||||
drill_Z = Units.Quantity(params['Z'], FreeCAD.Units.Length)
|
||||
drill_R = Units.Quantity(params['R'], FreeCAD.Units.Length)
|
||||
drill_F = Units.Quantity(params['F'], FreeCAD.Units.Velocity)
|
||||
if cmd == 'G82':
|
||||
drill_DwellTime = params['P']
|
||||
elif cmd == 'G83':
|
||||
drill_Step = Units.Quantity(params['Q'], FreeCAD.Units.Length)
|
||||
drill_X = Units.Quantity(params["X"], FreeCAD.Units.Length)
|
||||
drill_Y = Units.Quantity(params["Y"], FreeCAD.Units.Length)
|
||||
drill_Z = Units.Quantity(params["Z"], FreeCAD.Units.Length)
|
||||
drill_R = Units.Quantity(params["R"], FreeCAD.Units.Length)
|
||||
drill_F = Units.Quantity(params["F"], FreeCAD.Units.Velocity)
|
||||
if cmd == "G82":
|
||||
drill_DwellTime = params["P"]
|
||||
elif cmd == "G83":
|
||||
drill_Step = Units.Quantity(params["Q"], FreeCAD.Units.Length)
|
||||
|
||||
# R less than Z is error
|
||||
if drill_R < drill_Z:
|
||||
Drill.gcode += linenumber() + '(drill cycle error: R less than Z )\n'
|
||||
Drill.gcode += linenumber() + "(drill cycle error: R less than Z )\n"
|
||||
return Drill.gcode
|
||||
|
||||
# Z height to retract to when drill cycle is done:
|
||||
if DRILL_RETRACT_MODE == 'G98' and CURRENT_Z > drill_R:
|
||||
if DRILL_RETRACT_MODE == "G98" and CURRENT_Z > drill_R:
|
||||
RETRACT_Z = CURRENT_Z
|
||||
else:
|
||||
RETRACT_Z = drill_R
|
||||
|
||||
# Z motion nested functions:
|
||||
def rapid_Z_to(new_Z):
|
||||
Drill.gcode += linenumber() + 'G0 Z'
|
||||
Drill.gcode += format(
|
||||
float(new_Z.getValueAs(UNIT_FORMAT)), strFormat) + '\n'
|
||||
Drill.gcode += linenumber() + "G0 Z"
|
||||
Drill.gcode += format(float(new_Z.getValueAs(UNIT_FORMAT)), strFormat) + "\n"
|
||||
|
||||
def feed_Z_to(new_Z):
|
||||
Drill.gcode += linenumber() + 'G1 Z'
|
||||
Drill.gcode += format(
|
||||
float(new_Z.getValueAs(UNIT_FORMAT)), strFormat) + ' F'
|
||||
Drill.gcode += format(
|
||||
float(drill_F.getValueAs(UNIT_FEED_FORMAT)), '.2f') + '\n'
|
||||
Drill.gcode += linenumber() + "G1 Z"
|
||||
Drill.gcode += format(float(new_Z.getValueAs(UNIT_FORMAT)), strFormat) + " F"
|
||||
Drill.gcode += format(float(drill_F.getValueAs(UNIT_FEED_FORMAT)), ".2f") + "\n"
|
||||
|
||||
# Make sure that Z is not below RETRACT_Z:
|
||||
if CURRENT_Z < RETRACT_Z:
|
||||
rapid_Z_to(RETRACT_Z)
|
||||
|
||||
# Rapid to hole position XY:
|
||||
Drill.gcode += linenumber() + 'G0 X'
|
||||
Drill.gcode += format(
|
||||
float(drill_X.getValueAs(UNIT_FORMAT)), strFormat) + ' Y'
|
||||
Drill.gcode += format(
|
||||
float(drill_Y.getValueAs(UNIT_FORMAT)), strFormat) + '\n'
|
||||
Drill.gcode += linenumber() + "G0 X"
|
||||
Drill.gcode += format(float(drill_X.getValueAs(UNIT_FORMAT)), strFormat) + " Y"
|
||||
Drill.gcode += format(float(drill_Y.getValueAs(UNIT_FORMAT)), strFormat) + "\n"
|
||||
|
||||
# Rapid to R:
|
||||
rapid_Z_to(drill_R)
|
||||
@@ -728,13 +747,13 @@ def drill_translate(outlist, cmd, params):
|
||||
# * G99 After the hole has been drilled, retract to R height *
|
||||
# * Select G99 only if safe to move from hole to hole at the R height *
|
||||
# *************************************************************************
|
||||
if cmd in ('G81', 'G82'):
|
||||
if cmd in ("G81", "G82"):
|
||||
feed_Z_to(drill_Z) # Drill hole in one step
|
||||
if cmd == 'G82': # Dwell time delay at the bottom of the hole
|
||||
Drill.gcode += linenumber() + 'G4 S' + str(drill_DwellTime) + '\n'
|
||||
if cmd == "G82": # Dwell time delay at the bottom of the hole
|
||||
Drill.gcode += linenumber() + "G4 S" + str(drill_DwellTime) + "\n"
|
||||
# Marlin uses P for milliseconds, S for seconds, change P to S
|
||||
|
||||
elif cmd == 'G83': # Peck drill cycle:
|
||||
elif cmd == "G83": # Peck drill cycle:
|
||||
chip_Space = drill_Step * 0.5
|
||||
next_Stop_Z = drill_R - drill_Step
|
||||
while next_Stop_Z >= drill_Z:
|
||||
|
||||
Reference in New Issue
Block a user