Consistent black formatting of all Path python files
This commit is contained in:
@@ -41,7 +41,7 @@ import shlex
|
||||
from PathScripts import PostUtils
|
||||
from PathScripts import PathUtils
|
||||
|
||||
TOOLTIP = '''
|
||||
TOOLTIP = """
|
||||
This is a postprocessor file for the Path workbench. It is used to
|
||||
take a pseudo-gcode fragment outputted by a Path object, and output
|
||||
real GCode suitable for a High-Z S-1000T 4 Axis Mill by CNC-Step (www.cnc-step.com) using the KineticNC Control Software. This postprocessor, once placed
|
||||
@@ -50,21 +50,45 @@ FreeCAD, via the GUI importer or via python scripts with:
|
||||
|
||||
import KineticNC_post
|
||||
KineticNC_post.export(object,"/path/to/file.ncc","")
|
||||
'''
|
||||
"""
|
||||
|
||||
now = datetime.datetime.now()
|
||||
|
||||
parser = argparse.ArgumentParser(prog='linuxcnc', add_help=False)
|
||||
parser.add_argument('--no-header', action='store_true', help='suppress header output')
|
||||
parser.add_argument('--no-comments', action='store_true', help='suppress comment output')
|
||||
parser.add_argument('--line-numbers', action='store_true', help='prefix with line numbers')
|
||||
parser.add_argument('--no-show-editor', action='store_true', help='don\'t pop up editor before writing output')
|
||||
parser.add_argument('--precision', default='3', help='number of digits of precision, default=3')
|
||||
parser.add_argument('--preamble', help='set commands to be issued before the first command, default="G17\nG90"')
|
||||
parser.add_argument('--postamble', help='set commands to be issued after the last command, default="M05\nG17 G90\nM2"')
|
||||
parser.add_argument('--inches', action='store_true', help='Convert output for US imperial mode (G20)')
|
||||
parser.add_argument('--modal', action='store_true', help='Output the Same G-command Name USE NonModal Mode')
|
||||
parser.add_argument('--axis-modal', action='store_true', help='Output the Same Axis Value Mode')
|
||||
parser = argparse.ArgumentParser(prog="linuxcnc", add_help=False)
|
||||
parser.add_argument("--no-header", action="store_true", help="suppress header output")
|
||||
parser.add_argument(
|
||||
"--no-comments", action="store_true", help="suppress comment output"
|
||||
)
|
||||
parser.add_argument(
|
||||
"--line-numbers", action="store_true", help="prefix with line numbers"
|
||||
)
|
||||
parser.add_argument(
|
||||
"--no-show-editor",
|
||||
action="store_true",
|
||||
help="don't pop up editor before writing output",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--precision", default="3", help="number of digits of precision, default=3"
|
||||
)
|
||||
parser.add_argument(
|
||||
"--preamble",
|
||||
help='set commands to be issued before the first command, default="G17\nG90"',
|
||||
)
|
||||
parser.add_argument(
|
||||
"--postamble",
|
||||
help='set commands to be issued after the last command, default="M05\nG17 G90\nM2"',
|
||||
)
|
||||
parser.add_argument(
|
||||
"--inches", action="store_true", help="Convert output for US imperial mode (G20)"
|
||||
)
|
||||
parser.add_argument(
|
||||
"--modal",
|
||||
action="store_true",
|
||||
help="Output the Same G-command Name USE NonModal Mode",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--axis-modal", action="store_true", help="Output the Same Axis Value Mode"
|
||||
)
|
||||
|
||||
TOOLTIP_ARGS = parser.format_help()
|
||||
|
||||
@@ -74,44 +98,46 @@ OUTPUT_HEADER = True
|
||||
OUTPUT_LINE_NUMBERS = False
|
||||
SHOW_EDITOR = True
|
||||
MODAL = False # if true commands are suppressed if the same as previous line.
|
||||
OUTPUT_DOUBLES = True # if false duplicate axis values are suppressed if the same as previous line.
|
||||
OUTPUT_DOUBLES = (
|
||||
True # if false duplicate axis values are suppressed if the same as previous line.
|
||||
)
|
||||
COMMAND_SPACE = " "
|
||||
LINENR = 100 # line number starting value
|
||||
|
||||
# These globals will be reflected in the Machine configuration of the project
|
||||
UNITS = "G21" # G21 for metric, G20 for us standard
|
||||
UNIT_SPEED_FORMAT = 'mm/min'
|
||||
UNIT_FORMAT = 'mm'
|
||||
UNIT_SPEED_FORMAT = "mm/min"
|
||||
UNIT_FORMAT = "mm"
|
||||
|
||||
MACHINE_NAME = "High-Z S-1000T"
|
||||
CORNER_MIN = {'x': 0, 'y': 0, 'z': 0}
|
||||
CORNER_MAX = {'x': 1000, 'y': 600, 'z': 300}
|
||||
CORNER_MIN = {"x": 0, "y": 0, "z": 0}
|
||||
CORNER_MAX = {"x": 1000, "y": 600, "z": 300}
|
||||
PRECISION = 3
|
||||
|
||||
# Preamble text will appear at the beginning of the GCODE output file.
|
||||
PREAMBLE = '''%
|
||||
PREAMBLE = """%
|
||||
G17 G21 G40 G49 G80 G90
|
||||
M08
|
||||
'''
|
||||
"""
|
||||
|
||||
# Postamble text will appear following the last operation.
|
||||
POSTAMBLE = '''M05 M09
|
||||
POSTAMBLE = """M05 M09
|
||||
G17 G90 G80 G40
|
||||
M30
|
||||
'''
|
||||
"""
|
||||
|
||||
# Pre operation text will be inserted before every operation
|
||||
PRE_OPERATION = ''''''
|
||||
PRE_OPERATION = """"""
|
||||
|
||||
# Post operation text will be inserted after every operation
|
||||
POST_OPERATION = ''''''
|
||||
POST_OPERATION = """"""
|
||||
|
||||
# Tool Change commands will be inserted before a tool change
|
||||
TOOL_CHANGE = '''M05
|
||||
M09'''
|
||||
TOOL_CHANGE = """M05
|
||||
M09"""
|
||||
|
||||
# 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
|
||||
|
||||
|
||||
@@ -146,14 +172,14 @@ def processArguments(argstring):
|
||||
if args.postamble is not None:
|
||||
POSTAMBLE = args.postamble
|
||||
if args.inches:
|
||||
UNITS = 'G20'
|
||||
UNIT_SPEED_FORMAT = 'in/min'
|
||||
UNIT_FORMAT = 'in'
|
||||
UNITS = "G20"
|
||||
UNIT_SPEED_FORMAT = "in/min"
|
||||
UNIT_FORMAT = "in"
|
||||
PRECISION = 4
|
||||
if args.modal:
|
||||
MODAL = True
|
||||
if args.axis_modal:
|
||||
print ('here')
|
||||
print("here")
|
||||
OUTPUT_DOUBLES = False
|
||||
|
||||
except:
|
||||
@@ -171,7 +197,11 @@ def export(objectslist, filename, argstring):
|
||||
|
||||
for obj in objectslist:
|
||||
if not hasattr(obj, "Path"):
|
||||
print("the object " + obj.Name + " is not a path. Please select only path and Compounds.")
|
||||
print(
|
||||
"the object "
|
||||
+ obj.Name
|
||||
+ " is not a path. Please select only path and Compounds."
|
||||
)
|
||||
return None
|
||||
|
||||
print("postprocessing...")
|
||||
@@ -195,7 +225,7 @@ def export(objectslist, filename, argstring):
|
||||
# fetch machine details
|
||||
job = PathUtils.findParentJob(obj)
|
||||
|
||||
myMachine = 'not set'
|
||||
myMachine = "not set"
|
||||
|
||||
if hasattr(job, "MachineName"):
|
||||
myMachine = job.MachineName
|
||||
@@ -203,17 +233,20 @@ def export(objectslist, filename, argstring):
|
||||
if hasattr(job, "MachineUnits"):
|
||||
if job.MachineUnits == "Metric":
|
||||
UNITS = "G21"
|
||||
UNIT_FORMAT = 'mm'
|
||||
UNIT_SPEED_FORMAT = 'mm/min'
|
||||
UNIT_FORMAT = "mm"
|
||||
UNIT_SPEED_FORMAT = "mm/min"
|
||||
else:
|
||||
UNITS = "G20"
|
||||
UNIT_FORMAT = 'in'
|
||||
UNIT_SPEED_FORMAT = 'in/min'
|
||||
UNIT_FORMAT = "in"
|
||||
UNIT_SPEED_FORMAT = "in/min"
|
||||
|
||||
# do the pre_op
|
||||
if OUTPUT_COMMENTS:
|
||||
gcode += linenumber() + "(begin operation: %s)\n" % obj.Label
|
||||
gcode += linenumber() + "(machine: %s, %s)\n" % (myMachine, UNIT_SPEED_FORMAT)
|
||||
gcode += linenumber() + "(machine: %s, %s)\n" % (
|
||||
myMachine,
|
||||
UNIT_SPEED_FORMAT,
|
||||
)
|
||||
for line in PRE_OPERATION.splitlines(True):
|
||||
gcode += linenumber() + line
|
||||
|
||||
@@ -244,7 +277,7 @@ def export(objectslist, filename, argstring):
|
||||
|
||||
print("done postprocessing.")
|
||||
|
||||
if not filename == '-':
|
||||
if not filename == "-":
|
||||
gfile = pythonopen(filename, "w")
|
||||
gfile.write(final)
|
||||
gfile.close()
|
||||
@@ -269,12 +302,30 @@ def parse(pathobj):
|
||||
|
||||
out = ""
|
||||
lastcommand = None
|
||||
precision_string = '.' + str(PRECISION) + 'f'
|
||||
precision_string = "." + str(PRECISION) + "f"
|
||||
currLocation = {} # keep track for no doubles
|
||||
|
||||
# the order of parameters
|
||||
# linuxcnc doesn't want K properties on XY plane Arcs need work.
|
||||
params = ['X', 'Y', 'Z', 'A', 'B', 'C', 'I', 'J', 'F', 'S', 'T', 'Q', 'R', 'L', 'H', 'D', 'P']
|
||||
params = [
|
||||
"X",
|
||||
"Y",
|
||||
"Z",
|
||||
"A",
|
||||
"B",
|
||||
"C",
|
||||
"I",
|
||||
"J",
|
||||
"F",
|
||||
"S",
|
||||
"T",
|
||||
"Q",
|
||||
"R",
|
||||
"L",
|
||||
"H",
|
||||
"D",
|
||||
"P",
|
||||
]
|
||||
firstmove = Path.Command("G0", {"X": -1, "Y": -1, "Z": -1, "F": 0.0})
|
||||
currLocation.update(firstmove.Parameters) # set First location Parameters
|
||||
|
||||
@@ -304,41 +355,64 @@ def parse(pathobj):
|
||||
if command == lastcommand:
|
||||
outstring.pop(0)
|
||||
|
||||
if c.Name[0] == '(' and not OUTPUT_COMMENTS: # command is a comment
|
||||
if c.Name[0] == "(" and not OUTPUT_COMMENTS: # command is a comment
|
||||
continue
|
||||
|
||||
# Now add the remaining parameters in order
|
||||
for param in params:
|
||||
if param in c.Parameters:
|
||||
if param == 'F' and (currLocation[param] != c.Parameters[param] or OUTPUT_DOUBLES):
|
||||
if c.Name not in ["G0", "G00"]: # linuxcnc doesn't use rapid speeds
|
||||
speed = Units.Quantity(c.Parameters['F'], FreeCAD.Units.Velocity)
|
||||
if param == "F" and (
|
||||
currLocation[param] != c.Parameters[param] or OUTPUT_DOUBLES
|
||||
):
|
||||
if c.Name not in [
|
||||
"G0",
|
||||
"G00",
|
||||
]: # linuxcnc doesn't use rapid speeds
|
||||
speed = Units.Quantity(
|
||||
c.Parameters["F"], FreeCAD.Units.Velocity
|
||||
)
|
||||
if speed.getValueAs(UNIT_SPEED_FORMAT) > 0.0:
|
||||
outstring.append(param + format(float(speed.getValueAs(UNIT_SPEED_FORMAT)), precision_string))
|
||||
outstring.append(
|
||||
param
|
||||
+ format(
|
||||
float(speed.getValueAs(UNIT_SPEED_FORMAT)),
|
||||
precision_string,
|
||||
)
|
||||
)
|
||||
else:
|
||||
continue
|
||||
elif param == 'T':
|
||||
outstring.append(param + str(int(c.Parameters['T'])))
|
||||
elif param == 'H':
|
||||
outstring.append(param + str(int(c.Parameters['H'])))
|
||||
elif param == 'D':
|
||||
outstring.append(param + str(int(c.Parameters['D'])))
|
||||
elif param == 'S':
|
||||
outstring.append(param + str(int(c.Parameters['S'])))
|
||||
elif param == "T":
|
||||
outstring.append(param + str(int(c.Parameters["T"])))
|
||||
elif param == "H":
|
||||
outstring.append(param + str(int(c.Parameters["H"])))
|
||||
elif param == "D":
|
||||
outstring.append(param + str(int(c.Parameters["D"])))
|
||||
elif param == "S":
|
||||
outstring.append(param + str(int(c.Parameters["S"])))
|
||||
else:
|
||||
if (not OUTPUT_DOUBLES) and (param in currLocation) and (currLocation[param] == c.Parameters[param]):
|
||||
if (
|
||||
(not OUTPUT_DOUBLES)
|
||||
and (param in currLocation)
|
||||
and (currLocation[param] == c.Parameters[param])
|
||||
):
|
||||
continue
|
||||
else:
|
||||
pos = Units.Quantity(c.Parameters[param], FreeCAD.Units.Length)
|
||||
pos = Units.Quantity(
|
||||
c.Parameters[param], FreeCAD.Units.Length
|
||||
)
|
||||
outstring.append(
|
||||
param + format(float(pos.getValueAs(UNIT_FORMAT)), precision_string))
|
||||
param
|
||||
+ format(
|
||||
float(pos.getValueAs(UNIT_FORMAT)), precision_string
|
||||
)
|
||||
)
|
||||
|
||||
# store the latest command
|
||||
lastcommand = command
|
||||
currLocation.update(c.Parameters)
|
||||
|
||||
# Check for Tool Change:
|
||||
if command == 'M6':
|
||||
if command == "M6":
|
||||
# if OUTPUT_COMMENTS:
|
||||
# out += linenumber() + "(begin toolchange)\n"
|
||||
for line in TOOL_CHANGE.splitlines(True):
|
||||
@@ -362,4 +436,5 @@ def parse(pathobj):
|
||||
|
||||
return out
|
||||
|
||||
|
||||
print(__name__ + " gcode postprocessor loaded.")
|
||||
|
||||
@@ -30,7 +30,7 @@ import datetime
|
||||
import PathScripts
|
||||
import PathScripts.PostUtils as PostUtils
|
||||
|
||||
TOOLTIP = '''
|
||||
TOOLTIP = """
|
||||
This is a postprocessor file for the Path workbench. It is used to
|
||||
take a pseudo-gcode fragment outputted by a Path object, and output
|
||||
real GCode suitable for a centroid 3 axis mill. This postprocessor, once placed
|
||||
@@ -39,9 +39,9 @@ FreeCAD, via the GUI importer or via python scripts with:
|
||||
|
||||
import centroid_post
|
||||
centroid_post.export(object,"/path/to/file.ncc","")
|
||||
'''
|
||||
"""
|
||||
|
||||
TOOLTIP_ARGS = '''
|
||||
TOOLTIP_ARGS = """
|
||||
Arguments for centroid:
|
||||
--header,--no-header ... output headers (--header)
|
||||
--comments,--no-comments ... output comments (--comments)
|
||||
@@ -50,7 +50,7 @@ Arguments for centroid:
|
||||
--feed-precision=1 ... number of digits of precision for feed rate. Default=1
|
||||
--axis-precision=4 ... number of digits of precision for axis moves. Default=4
|
||||
--inches ... Convert output for US imperial mode (G20)
|
||||
'''
|
||||
"""
|
||||
now = datetime.datetime.now()
|
||||
|
||||
# These globals set common customization preferences
|
||||
@@ -68,56 +68,58 @@ LINENR = 100 # line number starting value
|
||||
|
||||
# These globals will be reflected in the Machine configuration of the project
|
||||
UNITS = "G21" # G21 for metric, G20 for us standard
|
||||
UNIT_FORMAT = 'mm'
|
||||
UNIT_SPEED_FORMAT = 'mm/min'
|
||||
UNIT_FORMAT = "mm"
|
||||
UNIT_SPEED_FORMAT = "mm/min"
|
||||
MACHINE_NAME = "Centroid"
|
||||
CORNER_MIN = {'x': -609.6, 'y': -152.4, 'z': 0} # use metric for internal units
|
||||
CORNER_MAX = {'x': 609.6, 'y': 152.4, 'z': 304.8} # use metric for internal units
|
||||
CORNER_MIN = {"x": -609.6, "y": -152.4, "z": 0} # use metric for internal units
|
||||
CORNER_MAX = {"x": 609.6, "y": 152.4, "z": 304.8} # use metric for internal units
|
||||
AXIS_PRECISION = 4
|
||||
FEED_PRECISION = 1
|
||||
SPINDLE_DECIMALS = 0
|
||||
|
||||
COMMENT = ";"
|
||||
|
||||
HEADER = '''
|
||||
HEADER = """
|
||||
;Exported by FreeCAD
|
||||
;Post Processor: {}
|
||||
;CAM file: {}
|
||||
;Output Time: {}
|
||||
'''.format(__name__, FreeCAD.ActiveDocument.FileName, str(now))
|
||||
""".format(
|
||||
__name__, FreeCAD.ActiveDocument.FileName, str(now)
|
||||
)
|
||||
|
||||
# Preamble text will appear at the beginning of the GCODE output file.
|
||||
PREAMBLE = '''G53 G00 G17
|
||||
'''
|
||||
PREAMBLE = """G53 G00 G17
|
||||
"""
|
||||
|
||||
# Postamble text will appear following the last operation.
|
||||
POSTAMBLE = '''M99
|
||||
'''
|
||||
POSTAMBLE = """M99
|
||||
"""
|
||||
|
||||
TOOLRETURN = '''M5
|
||||
TOOLRETURN = """M5
|
||||
M25
|
||||
G49 H0
|
||||
''' # spindle off,height offset canceled,spindle retracted (M25 is a centroid command to retract spindle)
|
||||
""" # spindle off,height offset canceled,spindle retracted (M25 is a centroid command to retract spindle)
|
||||
|
||||
ZAXISRETURN = '''G91 G28 X0 Z0
|
||||
ZAXISRETURN = """G91 G28 X0 Z0
|
||||
G90
|
||||
'''
|
||||
"""
|
||||
|
||||
SAFETYBLOCK = '''G90 G80 G40 G49
|
||||
'''
|
||||
SAFETYBLOCK = """G90 G80 G40 G49
|
||||
"""
|
||||
|
||||
# Pre operation text will be inserted before every operation
|
||||
PRE_OPERATION = ''''''
|
||||
PRE_OPERATION = """"""
|
||||
|
||||
# Post operation text will be inserted after every operation
|
||||
POST_OPERATION = ''''''
|
||||
POST_OPERATION = """"""
|
||||
|
||||
# Tool Change commands will be inserted before a tool change
|
||||
TOOL_CHANGE = ''''''
|
||||
TOOL_CHANGE = """"""
|
||||
|
||||
|
||||
# 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
|
||||
|
||||
|
||||
@@ -133,30 +135,30 @@ def processArguments(argstring):
|
||||
global UNITS
|
||||
|
||||
for arg in argstring.split():
|
||||
if arg == '--header':
|
||||
if arg == "--header":
|
||||
OUTPUT_HEADER = True
|
||||
elif arg == '--no-header':
|
||||
elif arg == "--no-header":
|
||||
OUTPUT_HEADER = False
|
||||
elif arg == '--comments':
|
||||
elif arg == "--comments":
|
||||
OUTPUT_COMMENTS = True
|
||||
elif arg == '--no-comments':
|
||||
elif arg == "--no-comments":
|
||||
OUTPUT_COMMENTS = False
|
||||
elif arg == '--line-numbers':
|
||||
elif arg == "--line-numbers":
|
||||
OUTPUT_LINE_NUMBERS = True
|
||||
elif arg == '--no-line-numbers':
|
||||
elif arg == "--no-line-numbers":
|
||||
OUTPUT_LINE_NUMBERS = False
|
||||
elif arg == '--show-editor':
|
||||
elif arg == "--show-editor":
|
||||
SHOW_EDITOR = True
|
||||
elif arg == '--no-show-editor':
|
||||
elif arg == "--no-show-editor":
|
||||
SHOW_EDITOR = False
|
||||
elif arg.split('=')[0] == '--axis-precision':
|
||||
AXIS_PRECISION = arg.split('=')[1]
|
||||
elif arg.split('=')[0] == '--feed-precision':
|
||||
FEED_PRECISION = arg.split('=')[1]
|
||||
elif arg == '--inches':
|
||||
UNITS = 'G20'
|
||||
UNIT_SPEED_FORMAT = 'in/min'
|
||||
UNIT_FORMAT = 'in'
|
||||
elif arg.split("=")[0] == "--axis-precision":
|
||||
AXIS_PRECISION = arg.split("=")[1]
|
||||
elif arg.split("=")[0] == "--feed-precision":
|
||||
FEED_PRECISION = arg.split("=")[1]
|
||||
elif arg == "--inches":
|
||||
UNITS = "G20"
|
||||
UNIT_SPEED_FORMAT = "in/min"
|
||||
UNIT_FORMAT = "in"
|
||||
|
||||
|
||||
def export(objectslist, filename, argstring):
|
||||
@@ -179,7 +181,9 @@ def export(objectslist, filename, argstring):
|
||||
# Write the preamble
|
||||
if OUTPUT_COMMENTS:
|
||||
for item in objectslist:
|
||||
if hasattr(item, "Proxy") and isinstance(item.Proxy, PathScripts.PathToolController.ToolController):
|
||||
if hasattr(item, "Proxy") and isinstance(
|
||||
item.Proxy, PathScripts.PathToolController.ToolController
|
||||
):
|
||||
gcode += ";T{}={}\n".format(item.ToolNumber, item.Name)
|
||||
gcode += linenumber() + ";begin preamble\n"
|
||||
for line in PREAMBLE.splitlines(True):
|
||||
@@ -226,7 +230,7 @@ def export(objectslist, filename, argstring):
|
||||
|
||||
print("done postprocessing.")
|
||||
|
||||
if not filename == '-':
|
||||
if not filename == "-":
|
||||
gfile = pythonopen(filename, "w")
|
||||
gfile.write(final)
|
||||
gfile.close()
|
||||
@@ -245,12 +249,12 @@ def linenumber():
|
||||
def parse(pathobj):
|
||||
out = ""
|
||||
lastcommand = None
|
||||
axis_precision_string = '.' + str(AXIS_PRECISION) + 'f'
|
||||
feed_precision_string = '.' + str(FEED_PRECISION) + 'f'
|
||||
axis_precision_string = "." + str(AXIS_PRECISION) + "f"
|
||||
feed_precision_string = "." + str(FEED_PRECISION) + "f"
|
||||
# params = ['X','Y','Z','A','B','I','J','K','F','S'] #This list control
|
||||
# the order of parameters
|
||||
# centroid doesn't want K properties on XY plane Arcs need work.
|
||||
params = ['X', 'Y', 'Z', 'A', 'B', 'I', 'J', 'F', 'S', 'T', 'Q', 'R', 'L', 'H']
|
||||
params = ["X", "Y", "Z", "A", "B", "I", "J", "F", "S", "T", "Q", "R", "L", "H"]
|
||||
|
||||
if hasattr(pathobj, "Group"): # We have a compound or project.
|
||||
# if OUTPUT_COMMENTS:
|
||||
@@ -271,7 +275,7 @@ def parse(pathobj):
|
||||
commandlist = [] # list of elements in the command, code and params.
|
||||
command = c.Name # command M or G code or comment string
|
||||
|
||||
if command[0] == '(':
|
||||
if command[0] == "(":
|
||||
command = PostUtils.fcoms(command, COMMENT)
|
||||
|
||||
commandlist.append(command)
|
||||
@@ -284,32 +288,50 @@ def parse(pathobj):
|
||||
# Now add the remaining parameters in order
|
||||
for param in params:
|
||||
if param in c.Parameters:
|
||||
if param == 'F':
|
||||
if c.Name not in ["G0", "G00"]: # centroid doesn't use rapid speeds
|
||||
speed = Units.Quantity(c.Parameters['F'], FreeCAD.Units.Velocity)
|
||||
if param == "F":
|
||||
if c.Name not in [
|
||||
"G0",
|
||||
"G00",
|
||||
]: # centroid doesn't use rapid speeds
|
||||
speed = Units.Quantity(
|
||||
c.Parameters["F"], FreeCAD.Units.Velocity
|
||||
)
|
||||
commandlist.append(
|
||||
param + format(float(speed.getValueAs(UNIT_SPEED_FORMAT)), feed_precision_string))
|
||||
elif param == 'H':
|
||||
commandlist.append(param + str(int(c.Parameters['H'])))
|
||||
elif param == 'S':
|
||||
commandlist.append(param + PostUtils.fmt(c.Parameters['S'], SPINDLE_DECIMALS, "G21"))
|
||||
elif param == 'T':
|
||||
commandlist.append(param + str(int(c.Parameters['T'])))
|
||||
param
|
||||
+ format(
|
||||
float(speed.getValueAs(UNIT_SPEED_FORMAT)),
|
||||
feed_precision_string,
|
||||
)
|
||||
)
|
||||
elif param == "H":
|
||||
commandlist.append(param + str(int(c.Parameters["H"])))
|
||||
elif param == "S":
|
||||
commandlist.append(
|
||||
param
|
||||
+ PostUtils.fmt(c.Parameters["S"], SPINDLE_DECIMALS, "G21")
|
||||
)
|
||||
elif param == "T":
|
||||
commandlist.append(param + str(int(c.Parameters["T"])))
|
||||
else:
|
||||
pos = Units.Quantity(c.Parameters[param], FreeCAD.Units.Length)
|
||||
commandlist.append(
|
||||
param + format(float(pos.getValueAs(UNIT_FORMAT)), axis_precision_string))
|
||||
param
|
||||
+ format(
|
||||
float(pos.getValueAs(UNIT_FORMAT)),
|
||||
axis_precision_string,
|
||||
)
|
||||
)
|
||||
outstr = str(commandlist)
|
||||
outstr = outstr.replace('[', '')
|
||||
outstr = outstr.replace(']', '')
|
||||
outstr = outstr.replace("'", '')
|
||||
outstr = outstr.replace(",", '')
|
||||
outstr = outstr.replace("[", "")
|
||||
outstr = outstr.replace("]", "")
|
||||
outstr = outstr.replace("'", "")
|
||||
outstr = outstr.replace(",", "")
|
||||
|
||||
# store the latest command
|
||||
lastcommand = command
|
||||
|
||||
# Check for Tool Change:
|
||||
if command == 'M6':
|
||||
if command == "M6":
|
||||
# if OUTPUT_COMMENTS:
|
||||
# out += linenumber() + "(begin toolchange)\n"
|
||||
for line in TOOL_CHANGE.splitlines(True):
|
||||
|
||||
@@ -24,25 +24,26 @@ import FreeCAD
|
||||
import Path
|
||||
import PathScripts.PostUtils as PostUtils
|
||||
|
||||
TOOLTIP = '''Example Post, using Path.Commands instead of Path.toGCode strings for Path gcode output.'''
|
||||
TOOLTIP = """Example Post, using Path.Commands instead of Path.toGCode strings for Path gcode output."""
|
||||
|
||||
SHOW_EDITOR = True
|
||||
|
||||
|
||||
def fmt(num):
|
||||
fnum = ""
|
||||
fnum += '%.3f' % (num)
|
||||
fnum += "%.3f" % (num)
|
||||
return fnum
|
||||
|
||||
|
||||
def ffmt(num):
|
||||
fnum = ""
|
||||
fnum += '%.1f' % (num)
|
||||
fnum += "%.1f" % (num)
|
||||
return fnum
|
||||
|
||||
|
||||
class saveVals(object):
|
||||
''' save command info for modal output'''
|
||||
"""save command info for modal output"""
|
||||
|
||||
def __init__(self, command):
|
||||
self.com = command.Name
|
||||
self.params = command.Parameters
|
||||
@@ -57,55 +58,55 @@ def lineout(command, oldvals, modal):
|
||||
line += ""
|
||||
else:
|
||||
line += str(command.Name)
|
||||
if command.Name == 'M6':
|
||||
line += 'T' + str(int(command.Parameters['T']))
|
||||
if command.Name == 'M3':
|
||||
line += 'S' + str(ffmt(command.Parameters['S']))
|
||||
if command.Name == 'M4':
|
||||
line += 'S' + str(ffmt(command.Parameters['S']))
|
||||
if 'X' in command.Parameters:
|
||||
line += "X" + str(fmt(command.Parameters['X']))
|
||||
if 'Y' in command.Parameters:
|
||||
line += "Y" + str(fmt(command.Parameters['Y']))
|
||||
if 'Z' in command.Parameters:
|
||||
line += "Z" + str(fmt(command.Parameters['Z']))
|
||||
if 'I' in command.Parameters:
|
||||
line += "I" + str(fmt(command.Parameters['I']))
|
||||
if 'J' in command.Parameters:
|
||||
line += "J" + str(fmt(command.Parameters['J']))
|
||||
if 'F' in command.Parameters:
|
||||
line += "F" + str(ffmt(command.Parameters['F']))
|
||||
if command.Name == "M6":
|
||||
line += "T" + str(int(command.Parameters["T"]))
|
||||
if command.Name == "M3":
|
||||
line += "S" + str(ffmt(command.Parameters["S"]))
|
||||
if command.Name == "M4":
|
||||
line += "S" + str(ffmt(command.Parameters["S"]))
|
||||
if "X" in command.Parameters:
|
||||
line += "X" + str(fmt(command.Parameters["X"]))
|
||||
if "Y" in command.Parameters:
|
||||
line += "Y" + str(fmt(command.Parameters["Y"]))
|
||||
if "Z" in command.Parameters:
|
||||
line += "Z" + str(fmt(command.Parameters["Z"]))
|
||||
if "I" in command.Parameters:
|
||||
line += "I" + str(fmt(command.Parameters["I"]))
|
||||
if "J" in command.Parameters:
|
||||
line += "J" + str(fmt(command.Parameters["J"]))
|
||||
if "F" in command.Parameters:
|
||||
line += "F" + str(ffmt(command.Parameters["F"]))
|
||||
return line
|
||||
|
||||
|
||||
def export(obj, filename, argstring):
|
||||
modal = True
|
||||
gcode = ''
|
||||
safetyblock1 = 'G90G40G49\n'
|
||||
gcode = ""
|
||||
safetyblock1 = "G90G40G49\n"
|
||||
gcode += safetyblock1
|
||||
|
||||
units = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Units")
|
||||
if units.GetInt('UserSchema') == 0:
|
||||
firstcommand = Path.Command('G21') # metric mode
|
||||
if units.GetInt("UserSchema") == 0:
|
||||
firstcommand = Path.Command("G21") # metric mode
|
||||
else:
|
||||
firstcommand = Path.Command('G20') # inch mode
|
||||
firstcommand = Path.Command("G20") # inch mode
|
||||
oldvals = saveVals(firstcommand) # save first command for modal use
|
||||
fp = obj[0]
|
||||
gcode += firstcommand.Name
|
||||
|
||||
if hasattr(fp, "Path"):
|
||||
for c in fp.Path.Commands:
|
||||
gcode += lineout(c, oldvals, modal) + '\n'
|
||||
gcode += lineout(c, oldvals, modal) + "\n"
|
||||
oldvals = saveVals(c)
|
||||
gcode += 'M2\n'
|
||||
gcode += "M2\n"
|
||||
gfile = open(filename, "w")
|
||||
gfile.write(gcode)
|
||||
gfile.close()
|
||||
else:
|
||||
FreeCAD.Console.PrintError('Select a path object and try again\n')
|
||||
FreeCAD.Console.PrintError("Select a path object and try again\n")
|
||||
|
||||
if SHOW_EDITOR:
|
||||
FreeCAD.Console.PrintMessage('Editor Activated\n')
|
||||
FreeCAD.Console.PrintMessage("Editor Activated\n")
|
||||
dia = PostUtils.GCodeEditorDialog()
|
||||
dia.editor.setText(gcode)
|
||||
dia.exec_()
|
||||
|
||||
@@ -23,12 +23,12 @@
|
||||
|
||||
from __future__ import print_function
|
||||
|
||||
TOOLTIP='''
|
||||
TOOLTIP = """
|
||||
Dumper is an extremely simple postprocessor file for the Path workbench. It is used
|
||||
to dump the command list from one or more Path objects for simple inspection. This post
|
||||
doesn't do any manipulation of the path and doesn't write anything to disk. It just
|
||||
shows the dialog so you can see it. Useful for debugging, but not much else.
|
||||
'''
|
||||
"""
|
||||
import datetime
|
||||
from PathScripts import PostUtils
|
||||
|
||||
@@ -36,25 +36,29 @@ now = datetime.datetime.now()
|
||||
SHOW_EDITOR = True
|
||||
|
||||
# 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 export(objectslist, filename,argstring):
|
||||
def export(objectslist, filename, argstring):
|
||||
"called when freecad exports a list of objects"
|
||||
|
||||
output = '''(This output produced with the dump post processor)
|
||||
output = """(This output produced with the dump post processor)
|
||||
(Dump is useful for inspecting the raw commands in your paths)
|
||||
(but is not useful for driving machines.)
|
||||
(Consider setting a default postprocessor in your project or )
|
||||
(exporting your paths using a specific post that matches your machine)
|
||||
|
||||
'''
|
||||
"""
|
||||
|
||||
for obj in objectslist:
|
||||
|
||||
if not hasattr(obj, "Path"):
|
||||
print("the object " + obj.Name + " is not a path. Please select only path and Compounds.")
|
||||
print(
|
||||
"the object "
|
||||
+ obj.Name
|
||||
+ " is not a path. Please select only path and Compounds."
|
||||
)
|
||||
return
|
||||
print("postprocessing...")
|
||||
output += parse(obj)
|
||||
@@ -84,7 +88,9 @@ def parse(pathobj):
|
||||
return out
|
||||
else: # parsing simple path
|
||||
|
||||
if not hasattr(pathobj, "Path"): # groups might contain non-path things like stock.
|
||||
if not hasattr(
|
||||
pathobj, "Path"
|
||||
): # groups might contain non-path things like stock.
|
||||
return out
|
||||
|
||||
out += "(Path: " + pathobj.Label + ")\n"
|
||||
@@ -93,4 +99,5 @@ def parse(pathobj):
|
||||
out += str(c) + "\n"
|
||||
return out
|
||||
|
||||
|
||||
# print(__name__ + " gcode postprocessor loaded.")
|
||||
|
||||
@@ -38,7 +38,7 @@ import datetime
|
||||
import shlex
|
||||
from PathScripts import PostUtils
|
||||
|
||||
TOOLTIP = '''
|
||||
TOOLTIP = """
|
||||
This is a postprocessor file for the Path workbench. It is used to
|
||||
take a pseudo-gcode fragment outputted by a Path object, and output
|
||||
real GCode suitable for a Tree Journyman 325 3 axis mill with Dynapath 20
|
||||
@@ -70,26 +70,35 @@ spaces add / prior to comments
|
||||
|
||||
import dynapath_post
|
||||
dynapath_post.export(object,"/path/to/file.ncc","")
|
||||
'''
|
||||
"""
|
||||
|
||||
parser = argparse.ArgumentParser(prog='dynapath_post',
|
||||
add_help=False)
|
||||
parser.add_argument('--no-header', action='store_true',
|
||||
help='suppress header output')
|
||||
parser.add_argument('--no-comments', action='store_true',
|
||||
help='suppress comment output')
|
||||
parser.add_argument('--line-numbers', action='store_true',
|
||||
help='prefix with line numbers')
|
||||
parser.add_argument('--no-show-editor', action='store_true',
|
||||
help='don\'t pop up editor before writing output')
|
||||
parser.add_argument('--precision', default='3',
|
||||
help='number of digits of precision, default=3')
|
||||
parser.add_argument('--preamble',
|
||||
help='set commands to be issued before the first command, default="G17\nG90\nG80\nG40"')
|
||||
parser.add_argument('--postamble',
|
||||
help='set commands to be issued after the last command, default="M09\nM05\nG80\nG40\nG17\nG90\nM30"')
|
||||
parser.add_argument('--inches', action='store_true',
|
||||
help='Convert output for US imperial mode (G20)')
|
||||
parser = argparse.ArgumentParser(prog="dynapath_post", add_help=False)
|
||||
parser.add_argument("--no-header", action="store_true", help="suppress header output")
|
||||
parser.add_argument(
|
||||
"--no-comments", action="store_true", help="suppress comment output"
|
||||
)
|
||||
parser.add_argument(
|
||||
"--line-numbers", action="store_true", help="prefix with line numbers"
|
||||
)
|
||||
parser.add_argument(
|
||||
"--no-show-editor",
|
||||
action="store_true",
|
||||
help="don't pop up editor before writing output",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--precision", default="3", help="number of digits of precision, default=3"
|
||||
)
|
||||
parser.add_argument(
|
||||
"--preamble",
|
||||
help='set commands to be issued before the first command, default="G17\nG90\nG80\nG40"',
|
||||
)
|
||||
parser.add_argument(
|
||||
"--postamble",
|
||||
help='set commands to be issued after the last command, default="M09\nM05\nG80\nG40\nG17\nG90\nM30"',
|
||||
)
|
||||
parser.add_argument(
|
||||
"--inches", action="store_true", help="Convert output for US imperial mode (G20)"
|
||||
)
|
||||
|
||||
|
||||
TOOLTIP_ARGS = parser.format_help()
|
||||
@@ -105,46 +114,46 @@ MODAL = False # if true commands are suppressed if the same as previous line.
|
||||
COMMAND_SPACE = " "
|
||||
LINENR = 1 # line number starting value
|
||||
|
||||
UNIT_SPEED_FORMAT = 'mm/min'
|
||||
UNIT_FORMAT = 'mm'
|
||||
UNIT_SPEED_FORMAT = "mm/min"
|
||||
UNIT_FORMAT = "mm"
|
||||
|
||||
# These globals will be reflected in the Machine configuration of the project
|
||||
UNITS = "G21" # G21 for metric, G20 for us standard
|
||||
MACHINE_NAME = "Tree MM"
|
||||
CORNER_MIN = {'x': -340, 'y': 0, 'z': 0}
|
||||
CORNER_MAX = {'x': 340, 'y': -355, 'z': -150}
|
||||
CORNER_MIN = {"x": -340, "y": 0, "z": 0}
|
||||
CORNER_MAX = {"x": 340, "y": -355, "z": -150}
|
||||
|
||||
# Preamble text will appear at the beginning of the GCODE output file.
|
||||
PREAMBLE = '''G17
|
||||
PREAMBLE = """G17
|
||||
G90
|
||||
;G90.1 ;needed for simulation only
|
||||
G80
|
||||
G40
|
||||
'''
|
||||
"""
|
||||
|
||||
# Postamble text will appear following the last operation.
|
||||
POSTAMBLE = '''M09
|
||||
POSTAMBLE = """M09
|
||||
M05
|
||||
G80
|
||||
G40
|
||||
G17
|
||||
G90
|
||||
M30
|
||||
'''
|
||||
"""
|
||||
|
||||
|
||||
# Pre operation text will be inserted before every operation
|
||||
PRE_OPERATION = ''''''
|
||||
PRE_OPERATION = """"""
|
||||
|
||||
# Post operation text will be inserted after every operation
|
||||
POST_OPERATION = ''''''
|
||||
POST_OPERATION = """"""
|
||||
|
||||
# Tool Change commands will be inserted before a tool change
|
||||
TOOL_CHANGE = ''''''
|
||||
TOOL_CHANGE = """"""
|
||||
|
||||
|
||||
# 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
|
||||
|
||||
|
||||
@@ -180,9 +189,9 @@ def processArguments(argstring):
|
||||
if args.postamble is not None:
|
||||
POSTAMBLE = args.postamble
|
||||
if args.inches:
|
||||
UNITS = 'G20'
|
||||
UNIT_SPEED_FORMAT = 'in/min'
|
||||
UNIT_FORMAT = 'in'
|
||||
UNITS = "G20"
|
||||
UNIT_SPEED_FORMAT = "in/min"
|
||||
UNIT_FORMAT = "in"
|
||||
PRECISION = 4
|
||||
|
||||
except Exception:
|
||||
@@ -200,7 +209,11 @@ def export(objectslist, filename, argstring):
|
||||
|
||||
for obj in objectslist:
|
||||
if not hasattr(obj, "Path"):
|
||||
print("the object " + obj.Name + " is not a path. Please select only path and Compounds.")
|
||||
print(
|
||||
"the object "
|
||||
+ obj.Name
|
||||
+ " is not a path. Please select only path and Compounds."
|
||||
)
|
||||
return
|
||||
|
||||
print("postprocessing...")
|
||||
@@ -224,7 +237,7 @@ def export(objectslist, filename, argstring):
|
||||
if OUTPUT_HEADER:
|
||||
gcode += linenumber() + "(Exported by FreeCAD)\n"
|
||||
gcode += linenumber() + "(Post Processor: " + __name__ + ")\n"
|
||||
gcode += linenumber() + "(Output Time:"+str(now)+")\n"
|
||||
gcode += linenumber() + "(Output Time:" + str(now) + ")\n"
|
||||
|
||||
# Write the preamble
|
||||
if OUTPUT_COMMENTS:
|
||||
@@ -256,7 +269,7 @@ def export(objectslist, filename, argstring):
|
||||
for line in POSTAMBLE.splitlines(True):
|
||||
gcode += linenumber() + line
|
||||
|
||||
print('show editor: {}'.format(SHOW_EDITOR))
|
||||
print("show editor: {}".format(SHOW_EDITOR))
|
||||
if FreeCAD.GuiUp and SHOW_EDITOR:
|
||||
dia = PostUtils.GCodeEditorDialog()
|
||||
dia.editor.setText(gcode)
|
||||
@@ -291,10 +304,10 @@ def parse(pathobj):
|
||||
out = ""
|
||||
lastcommand = None
|
||||
|
||||
precision_string = '.' + str(PRECISION) + 'f'
|
||||
precision_string = "." + str(PRECISION) + "f"
|
||||
|
||||
# params = ['X','Y','Z','A','B','I','J','K','F','S'] #This list control the order of parameters
|
||||
params = ['X', 'Y', 'Z', 'A', 'B', 'I', 'J', 'F', 'S', 'T', 'Q', 'R', 'L']
|
||||
params = ["X", "Y", "Z", "A", "B", "I", "J", "F", "S", "T", "Q", "R", "L"]
|
||||
|
||||
if hasattr(pathobj, "Group"): # We have a compound or project.
|
||||
if OUTPUT_COMMENTS:
|
||||
@@ -304,7 +317,9 @@ def parse(pathobj):
|
||||
return out
|
||||
else: # parsing simple path
|
||||
|
||||
if not hasattr(pathobj, "Path"): # groups might contain non-path things like stock.
|
||||
if not hasattr(
|
||||
pathobj, "Path"
|
||||
): # groups might contain non-path things like stock.
|
||||
return out
|
||||
|
||||
if OUTPUT_COMMENTS:
|
||||
@@ -322,24 +337,40 @@ def parse(pathobj):
|
||||
# Now add the remaining parameters in order
|
||||
for param in params:
|
||||
if param in c.Parameters:
|
||||
if param == 'F':
|
||||
speed = Units.Quantity(c.Parameters['F'], FreeCAD.Units.Velocity)
|
||||
if param == "F":
|
||||
speed = Units.Quantity(
|
||||
c.Parameters["F"], FreeCAD.Units.Velocity
|
||||
)
|
||||
if speed.getValueAs(UNIT_SPEED_FORMAT) > 0.0:
|
||||
outstring.append(param + format(float(speed.getValueAs(UNIT_SPEED_FORMAT)), precision_string))
|
||||
elif param == 'S':
|
||||
outstring.append(param + format(c.Parameters[param], precision_string))
|
||||
elif param == 'T':
|
||||
outstring.append(param + format(c.Parameters['T'], precision_string))
|
||||
outstring.append(
|
||||
param
|
||||
+ format(
|
||||
float(speed.getValueAs(UNIT_SPEED_FORMAT)),
|
||||
precision_string,
|
||||
)
|
||||
)
|
||||
elif param == "S":
|
||||
outstring.append(
|
||||
param + format(c.Parameters[param], precision_string)
|
||||
)
|
||||
elif param == "T":
|
||||
outstring.append(
|
||||
param + format(c.Parameters["T"], precision_string)
|
||||
)
|
||||
else:
|
||||
pos = Units.Quantity(c.Parameters[param], FreeCAD.Units.Length)
|
||||
outstring.append(
|
||||
param + format(float(pos.getValueAs(UNIT_FORMAT)), precision_string))
|
||||
param
|
||||
+ format(
|
||||
float(pos.getValueAs(UNIT_FORMAT)), precision_string
|
||||
)
|
||||
)
|
||||
|
||||
# store the latest command
|
||||
lastcommand = command
|
||||
|
||||
# Check for Tool Change:
|
||||
if command == 'M6':
|
||||
if command == "M6":
|
||||
if OUTPUT_COMMENTS:
|
||||
out += linenumber() + "(begin toolchange)\n"
|
||||
for line in TOOL_CHANGE.splitlines(True):
|
||||
|
||||
@@ -23,24 +23,25 @@
|
||||
|
||||
from __future__ import print_function
|
||||
|
||||
TOOLTIP='''
|
||||
TOOLTIP = """
|
||||
This is an example postprocessor file for the Path workbench. It is used
|
||||
to save a list of FreeCAD Path objects to a file.
|
||||
|
||||
Read the Path Workbench documentation to know how to convert Path objects
|
||||
to GCode.
|
||||
'''
|
||||
"""
|
||||
|
||||
import datetime
|
||||
|
||||
now = datetime.datetime.now()
|
||||
|
||||
|
||||
# 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 export(objectslist, filename,argstring):
|
||||
def export(objectslist, filename, argstring):
|
||||
"called when freecad exports a list of objects"
|
||||
if len(objectslist) > 1:
|
||||
print("This script is unable to write more than one Path object")
|
||||
@@ -62,7 +63,7 @@ def parse(inputstring):
|
||||
output = ""
|
||||
|
||||
# write some stuff first
|
||||
output += "N10 ;time:"+str(now)+"\n"
|
||||
output += "N10 ;time:" + str(now) + "\n"
|
||||
output += "N20 G17 G20 G80 G40 G90\n"
|
||||
output += "N30 (Exported by FreeCAD)\n"
|
||||
|
||||
@@ -99,4 +100,5 @@ def parse(inputstring):
|
||||
print("done postprocessing.")
|
||||
return output
|
||||
|
||||
|
||||
# print(__name__ + " gcode postprocessor loaded.")
|
||||
|
||||
@@ -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. This preprocessor will not add imported gcode to an existing
|
||||
@@ -30,7 +30,7 @@ job. For a more useful preprocessor, look at the gcode_pre.py file
|
||||
|
||||
Read the Path Workbench documentation to know how to create Path objects
|
||||
from GCode.
|
||||
'''
|
||||
"""
|
||||
|
||||
import os
|
||||
import Path
|
||||
@@ -46,7 +46,7 @@ if LEVEL == PathLog.Level.DEBUG:
|
||||
|
||||
|
||||
# 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
|
||||
|
||||
|
||||
|
||||
@@ -26,23 +26,24 @@
|
||||
|
||||
import datetime
|
||||
from PathScripts import PostUtils
|
||||
|
||||
now = datetime.datetime.now()
|
||||
|
||||
'''
|
||||
"""
|
||||
Generate g-code compatible with fablin from a Path.
|
||||
|
||||
import fablin_post
|
||||
fablin_post.export(object,"/path/to/file.ncc")
|
||||
'''
|
||||
"""
|
||||
|
||||
TOOLTIP_ARGS = '''
|
||||
TOOLTIP_ARGS = """
|
||||
Arguments for fablin:
|
||||
--rapids-feedrate ... feedrate to be used for rapids (e.g. --rapids-feedrate=300)
|
||||
--header,--no-header ... output headers (--header)
|
||||
--comments,--no-comments ... output comments (--comments)
|
||||
--line-numbers,--no-line-numbers ... prefix with line numbers (--no-lin-numbers)
|
||||
--show-editor, --no-show-editor ... pop up editor before writing output(--show-editor)
|
||||
'''
|
||||
"""
|
||||
|
||||
# These globals set common customization preferences
|
||||
OUTPUT_COMMENTS = False # Fablin does not support parenthesis, it will echo the command complaining. As a side effect the spinner may turn at a very reduced speed (do not ask me why).
|
||||
@@ -58,38 +59,38 @@ LINENR = 100 # line number starting value
|
||||
# These globals will be reflected in the Machine configuration of the project
|
||||
UNITS = "" # only metric, G20/G21 is ignored
|
||||
MACHINE_NAME = "FABLIN"
|
||||
CORNER_MIN = {'x': 0, 'y': 0, 'z': 0}
|
||||
CORNER_MAX = {'x': 500, 'y': 300, 'z': 300}
|
||||
CORNER_MIN = {"x": 0, "y": 0, "z": 0}
|
||||
CORNER_MAX = {"x": 500, "y": 300, "z": 300}
|
||||
|
||||
RAPID_MOVES = ['G0', 'G00']
|
||||
RAPID_MOVES = ["G0", "G00"]
|
||||
|
||||
RAPID_FEEDRATE = 10000
|
||||
|
||||
# Preamble text will appear at the beginning of the GCODE output file.
|
||||
PREAMBLE = '''G90
|
||||
'''
|
||||
PREAMBLE = """G90
|
||||
"""
|
||||
|
||||
# Postamble text will appear following the last operation.
|
||||
POSTAMBLE = '''M5
|
||||
POSTAMBLE = """M5
|
||||
G00 X-1.0 Y1.0
|
||||
G90
|
||||
'''
|
||||
"""
|
||||
|
||||
# These commands are ignored by commenting them out
|
||||
SUPPRESS_COMMANDS = ['G98', 'G80', 'M6', 'G17']
|
||||
SUPPRESS_COMMANDS = ["G98", "G80", "M6", "G17"]
|
||||
|
||||
# Pre operation text will be inserted before every operation
|
||||
PRE_OPERATION = ''''''
|
||||
PRE_OPERATION = """"""
|
||||
|
||||
# Post operation text will be inserted after every operation
|
||||
POST_OPERATION = ''''''
|
||||
POST_OPERATION = """"""
|
||||
|
||||
# Tool Change commands will be inserted before a tool change
|
||||
TOOL_CHANGE = ''''''
|
||||
TOOL_CHANGE = """"""
|
||||
|
||||
|
||||
# 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
|
||||
|
||||
|
||||
@@ -100,26 +101,26 @@ def processArguments(argstring):
|
||||
global SHOW_EDITOR
|
||||
global RAPID_FEEDRATE
|
||||
for arg in argstring.split():
|
||||
if arg == '--header':
|
||||
if arg == "--header":
|
||||
OUTPUT_HEADER = True
|
||||
elif arg == '--no-header':
|
||||
elif arg == "--no-header":
|
||||
OUTPUT_HEADER = False
|
||||
elif arg == '--comments':
|
||||
elif arg == "--comments":
|
||||
OUTPUT_COMMENTS = True
|
||||
elif arg == '--no-comments':
|
||||
elif arg == "--no-comments":
|
||||
OUTPUT_COMMENTS = False
|
||||
elif arg == '--line-numbers':
|
||||
elif arg == "--line-numbers":
|
||||
OUTPUT_LINE_NUMBERS = True
|
||||
elif arg == '--no-line-numbers':
|
||||
elif arg == "--no-line-numbers":
|
||||
OUTPUT_LINE_NUMBERS = False
|
||||
elif arg == '--show-editor':
|
||||
elif arg == "--show-editor":
|
||||
SHOW_EDITOR = True
|
||||
elif arg == '--no-show-editor':
|
||||
elif arg == "--no-show-editor":
|
||||
SHOW_EDITOR = False
|
||||
|
||||
params = arg.split('=')
|
||||
params = arg.split("=")
|
||||
|
||||
if params[0] == '--rapids-feedrate':
|
||||
if params[0] == "--rapids-feedrate":
|
||||
RAPID_FEEDRATE = params[1]
|
||||
|
||||
|
||||
@@ -128,7 +129,11 @@ def export(objectslist, filename, argstring):
|
||||
global UNITS
|
||||
for obj in objectslist:
|
||||
if not hasattr(obj, "Path"):
|
||||
print("the object " + obj.Name + " is not a path. Please select only path and Compounds.")
|
||||
print(
|
||||
"the object "
|
||||
+ obj.Name
|
||||
+ " is not a path. Please select only path and Compounds."
|
||||
)
|
||||
return
|
||||
|
||||
print("postprocessing...")
|
||||
@@ -216,7 +221,21 @@ def parse(pathobj):
|
||||
out = ""
|
||||
lastcommand = None
|
||||
|
||||
params = ['X', 'Y', 'Z', 'A', 'B', 'I', 'J', 'F', 'S', 'T', 'Q', 'R', 'L'] # linuxcnc doesn't want K properties on XY plane Arcs need work.
|
||||
params = [
|
||||
"X",
|
||||
"Y",
|
||||
"Z",
|
||||
"A",
|
||||
"B",
|
||||
"I",
|
||||
"J",
|
||||
"F",
|
||||
"S",
|
||||
"T",
|
||||
"Q",
|
||||
"R",
|
||||
"L",
|
||||
] # linuxcnc doesn't want K properties on XY plane Arcs need work.
|
||||
|
||||
if hasattr(pathobj, "Group"): # We have a compound or project.
|
||||
if OUTPUT_COMMENTS:
|
||||
@@ -226,7 +245,9 @@ def parse(pathobj):
|
||||
return out
|
||||
else: # parsing simple path
|
||||
|
||||
if not hasattr(pathobj, "Path"): # groups might contain non-path things like stock.
|
||||
if not hasattr(
|
||||
pathobj, "Path"
|
||||
): # groups might contain non-path things like stock.
|
||||
return out
|
||||
|
||||
if OUTPUT_COMMENTS:
|
||||
@@ -237,7 +258,7 @@ def parse(pathobj):
|
||||
command = c.Name
|
||||
|
||||
# fablin does not support parenthesis syntax, so removing that (pocket) in the agnostic gcode
|
||||
if command[0] == '(':
|
||||
if command[0] == "(":
|
||||
if not OUTPUT_COMMENTS:
|
||||
pass
|
||||
else:
|
||||
@@ -251,22 +272,22 @@ def parse(pathobj):
|
||||
# Now 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:
|
||||
outstring.append(param + format(c.Parameters['F'], '.2f'))
|
||||
elif param == 'T':
|
||||
outstring.append(param + str(c.Parameters['T']))
|
||||
outstring.append(param + format(c.Parameters["F"], ".2f"))
|
||||
elif param == "T":
|
||||
outstring.append(param + str(c.Parameters["T"]))
|
||||
else:
|
||||
outstring.append(param + format(c.Parameters[param], '.4f'))
|
||||
outstring.append(param + format(c.Parameters[param], ".4f"))
|
||||
|
||||
if command in RAPID_MOVES and command != lastcommand:
|
||||
outstring.append('F' + format(RAPID_FEEDRATE))
|
||||
outstring.append("F" + format(RAPID_FEEDRATE))
|
||||
|
||||
# store the latest command
|
||||
lastcommand = command
|
||||
|
||||
# Check for Tool Change:
|
||||
if command == 'M6':
|
||||
if command == "M6":
|
||||
if OUTPUT_COMMENTS:
|
||||
out += linenumber() + "(begin toolchange)\n"
|
||||
if not OUTPUT_TOOL_CHANGE:
|
||||
|
||||
@@ -31,7 +31,7 @@ import shlex
|
||||
import os.path
|
||||
from PathScripts import PostUtils
|
||||
|
||||
TOOLTIP = '''
|
||||
TOOLTIP = """
|
||||
This is a postprocessor file for the Path workbench. It is used to
|
||||
take a pseudo-gcode fragment outputted by a Path object, and output
|
||||
real GCode suitable should be suitable for most Fanuc controllers.
|
||||
@@ -42,22 +42,50 @@ python scripts with:
|
||||
|
||||
import fanuc_post
|
||||
fanuc_post.export(object,"/path/to/file.ncc","")
|
||||
'''
|
||||
"""
|
||||
|
||||
now = datetime.datetime.now()
|
||||
|
||||
parser = argparse.ArgumentParser(prog='fanuc', add_help=False)
|
||||
parser.add_argument('--no-header', action='store_true', help='suppress header output')
|
||||
parser.add_argument('--no-comments', action='store_true', help='suppress comment output')
|
||||
parser.add_argument('--line-numbers', action='store_true', help='prefix with line numbers')
|
||||
parser.add_argument('--no-show-editor', action='store_true', help='don\'t pop up editor before writing output')
|
||||
parser.add_argument('--precision', default='3', help='number of digits of precision, default=3')
|
||||
parser.add_argument('--preamble', help='set commands to be issued before the first command, default="G17\nG90"')
|
||||
parser.add_argument('--postamble', help='set commands to be issued after the last command, default="M05\nG17 G90\nM2"')
|
||||
parser.add_argument('--inches', action='store_true', help='Convert output for US imperial mode (G20)')
|
||||
parser.add_argument('--no-modal', action='store_true', help='Don\'t output the Same G-command Name USE NonModal Mode')
|
||||
parser.add_argument('--no-axis-modal', action='store_true', help='Don\'t output the Same Axis Value Mode')
|
||||
parser.add_argument('--no-tlo', action='store_true', help='suppress tool length offset (G43) following tool changes')
|
||||
parser = argparse.ArgumentParser(prog="fanuc", add_help=False)
|
||||
parser.add_argument("--no-header", action="store_true", help="suppress header output")
|
||||
parser.add_argument(
|
||||
"--no-comments", action="store_true", help="suppress comment output"
|
||||
)
|
||||
parser.add_argument(
|
||||
"--line-numbers", action="store_true", help="prefix with line numbers"
|
||||
)
|
||||
parser.add_argument(
|
||||
"--no-show-editor",
|
||||
action="store_true",
|
||||
help="don't pop up editor before writing output",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--precision", default="3", help="number of digits of precision, default=3"
|
||||
)
|
||||
parser.add_argument(
|
||||
"--preamble",
|
||||
help='set commands to be issued before the first command, default="G17\nG90"',
|
||||
)
|
||||
parser.add_argument(
|
||||
"--postamble",
|
||||
help='set commands to be issued after the last command, default="M05\nG17 G90\nM2"',
|
||||
)
|
||||
parser.add_argument(
|
||||
"--inches", action="store_true", help="Convert output for US imperial mode (G20)"
|
||||
)
|
||||
parser.add_argument(
|
||||
"--no-modal",
|
||||
action="store_true",
|
||||
help="Don't output the Same G-command Name USE NonModal Mode",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--no-axis-modal", action="store_true", help="Don't output the Same Axis Value Mode"
|
||||
)
|
||||
parser.add_argument(
|
||||
"--no-tlo",
|
||||
action="store_true",
|
||||
help="suppress tool length offset (G43) following tool changes",
|
||||
)
|
||||
|
||||
TOOLTIP_ARGS = parser.format_help()
|
||||
|
||||
@@ -67,19 +95,21 @@ OUTPUT_HEADER = True
|
||||
OUTPUT_LINE_NUMBERS = False
|
||||
SHOW_EDITOR = True
|
||||
MODAL = True # if true commands are suppressed if the same as previous line.
|
||||
USE_TLO = True # if true G43 will be output following tool changes
|
||||
OUTPUT_DOUBLES = False # if false duplicate axis values are suppressed if the same as previous line.
|
||||
USE_TLO = True # if true G43 will be output following tool changes
|
||||
OUTPUT_DOUBLES = (
|
||||
False # if false duplicate axis values are suppressed if the same as previous line.
|
||||
)
|
||||
COMMAND_SPACE = " "
|
||||
LINENR = 100 # line number starting value
|
||||
|
||||
# These globals will be reflected in the Machine configuration of the project
|
||||
UNITS = "G21" # G21 for metric, G20 for us standard
|
||||
UNIT_SPEED_FORMAT = 'mm/min'
|
||||
UNIT_FORMAT = 'mm'
|
||||
UNIT_SPEED_FORMAT = "mm/min"
|
||||
UNIT_FORMAT = "mm"
|
||||
|
||||
MACHINE_NAME = "fanuc"
|
||||
CORNER_MIN = {'x': 0, 'y': 0, 'z': 0}
|
||||
CORNER_MAX = {'x': 500, 'y': 300, 'z': 300}
|
||||
CORNER_MIN = {"x": 0, "y": 0, "z": 0}
|
||||
CORNER_MAX = {"x": 500, "y": 300, "z": 300}
|
||||
PRECISION = 3
|
||||
|
||||
# this global is used to pass spindle speed from the tool command into the machining command for
|
||||
@@ -87,27 +117,27 @@ PRECISION = 3
|
||||
tapSpeed = 0
|
||||
|
||||
# Preamble text will appear at the beginning of the GCODE output file.
|
||||
PREAMBLE = '''G17 G54 G40 G49 G80 G90
|
||||
'''
|
||||
PREAMBLE = """G17 G54 G40 G49 G80 G90
|
||||
"""
|
||||
|
||||
# Postamble text will appear following the last operation.
|
||||
POSTAMBLE = '''M05
|
||||
POSTAMBLE = """M05
|
||||
G17 G54 G90 G80 G40
|
||||
M6 T0
|
||||
M2
|
||||
'''
|
||||
"""
|
||||
|
||||
# Pre operation text will be inserted before every operation
|
||||
PRE_OPERATION = ''''''
|
||||
PRE_OPERATION = """"""
|
||||
|
||||
# Post operation text will be inserted after every operation
|
||||
POST_OPERATION = ''''''
|
||||
POST_OPERATION = """"""
|
||||
|
||||
# Tool Change commands will be inserted before a tool change
|
||||
TOOL_CHANGE = ''''''
|
||||
TOOL_CHANGE = """"""
|
||||
|
||||
# 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
|
||||
|
||||
|
||||
@@ -143,9 +173,9 @@ def processArguments(argstring):
|
||||
if args.postamble is not None:
|
||||
POSTAMBLE = args.postamble
|
||||
if args.inches:
|
||||
UNITS = 'G20'
|
||||
UNIT_SPEED_FORMAT = 'in/min'
|
||||
UNIT_FORMAT = 'in'
|
||||
UNITS = "G20"
|
||||
UNIT_SPEED_FORMAT = "in/min"
|
||||
UNIT_FORMAT = "in"
|
||||
PRECISION = 4
|
||||
if args.no_modal:
|
||||
MODAL = False
|
||||
@@ -171,7 +201,11 @@ def export(objectslist, filename, argstring):
|
||||
|
||||
for obj in objectslist:
|
||||
if not hasattr(obj, "Path"):
|
||||
print("the object " + obj.Name + " is not a path. Please select only path and Compounds.")
|
||||
print(
|
||||
"the object "
|
||||
+ obj.Name
|
||||
+ " is not a path. Please select only path and Compounds."
|
||||
)
|
||||
return None
|
||||
|
||||
print("postprocessing...")
|
||||
@@ -181,8 +215,15 @@ def export(objectslist, filename, argstring):
|
||||
if OUTPUT_HEADER:
|
||||
gcode += "%\n"
|
||||
gcode += ";\n"
|
||||
gcode += os.path.split(filename)[-1]+" ("+"FREECAD-FILENAME-GOES-HERE" + ", " + "JOB-NAME-GOES-HERE"+")\n"
|
||||
gcode += linenumber() + "("+filename.upper()+",EXPORTED BY FREECAD!)\n"
|
||||
gcode += (
|
||||
os.path.split(filename)[-1]
|
||||
+ " ("
|
||||
+ "FREECAD-FILENAME-GOES-HERE"
|
||||
+ ", "
|
||||
+ "JOB-NAME-GOES-HERE"
|
||||
+ ")\n"
|
||||
)
|
||||
gcode += linenumber() + "(" + filename.upper() + ",EXPORTED BY FREECAD!)\n"
|
||||
gcode += linenumber() + "(POST PROCESSOR: " + __name__.upper() + ")\n"
|
||||
gcode += linenumber() + "(OUTPUT TIME:" + str(now).upper() + ")\n"
|
||||
|
||||
@@ -196,23 +237,29 @@ def export(objectslist, filename, argstring):
|
||||
for obj in objectslist:
|
||||
|
||||
# Skip inactive operations
|
||||
if hasattr(obj, 'Active'):
|
||||
if hasattr(obj, "Active"):
|
||||
if not obj.Active:
|
||||
continue
|
||||
if hasattr(obj, 'Base') and hasattr(obj.Base, 'Active'):
|
||||
if hasattr(obj, "Base") and hasattr(obj.Base, "Active"):
|
||||
if not obj.Base.Active:
|
||||
continue
|
||||
|
||||
# do the pre_op
|
||||
if OUTPUT_COMMENTS:
|
||||
gcode += linenumber() + "(BEGIN OPERATION: %s)\n" % obj.Label.upper()
|
||||
gcode += linenumber() + "(MACHINE UNITS: %s)\n" % (UNIT_SPEED_FORMAT.upper())
|
||||
gcode += linenumber() + "(MACHINE UNITS: %s)\n" % (
|
||||
UNIT_SPEED_FORMAT.upper()
|
||||
)
|
||||
for line in PRE_OPERATION.splitlines(True):
|
||||
gcode += linenumber() + line
|
||||
|
||||
# get coolant mode
|
||||
coolantMode = 'None'
|
||||
if hasattr(obj, "CoolantMode") or hasattr(obj, 'Base') and hasattr(obj.Base, "CoolantMode"):
|
||||
coolantMode = "None"
|
||||
if (
|
||||
hasattr(obj, "CoolantMode")
|
||||
or hasattr(obj, "Base")
|
||||
and hasattr(obj.Base, "CoolantMode")
|
||||
):
|
||||
if hasattr(obj, "CoolantMode"):
|
||||
coolantMode = obj.CoolantMode
|
||||
else:
|
||||
@@ -220,12 +267,12 @@ def export(objectslist, filename, argstring):
|
||||
|
||||
# turn coolant on if required
|
||||
if OUTPUT_COMMENTS:
|
||||
if not coolantMode == 'None':
|
||||
gcode += linenumber() + '(COOLANT ON:' + coolantMode.upper() + ')\n'
|
||||
if coolantMode == 'Flood':
|
||||
gcode += linenumber() + 'M8' + '\n'
|
||||
if coolantMode == 'Mist':
|
||||
gcode += linenumber() + 'M7' + '\n'
|
||||
if not coolantMode == "None":
|
||||
gcode += linenumber() + "(COOLANT ON:" + coolantMode.upper() + ")\n"
|
||||
if coolantMode == "Flood":
|
||||
gcode += linenumber() + "M8" + "\n"
|
||||
if coolantMode == "Mist":
|
||||
gcode += linenumber() + "M7" + "\n"
|
||||
|
||||
# process the operation gcode
|
||||
gcode += parse(obj)
|
||||
@@ -237,10 +284,10 @@ def export(objectslist, filename, argstring):
|
||||
gcode += linenumber() + line
|
||||
|
||||
# turn coolant off if required
|
||||
if not coolantMode == 'None':
|
||||
if not coolantMode == "None":
|
||||
if OUTPUT_COMMENTS:
|
||||
gcode += linenumber() + '(COOLANT OFF:' + coolantMode.upper() + ')\n'
|
||||
gcode += linenumber() +'M9' + '\n'
|
||||
gcode += linenumber() + "(COOLANT OFF:" + coolantMode.upper() + ")\n"
|
||||
gcode += linenumber() + "M9" + "\n"
|
||||
|
||||
# do the post_amble
|
||||
if OUTPUT_COMMENTS:
|
||||
@@ -262,7 +309,7 @@ def export(objectslist, filename, argstring):
|
||||
|
||||
print("done postprocessing.")
|
||||
|
||||
if not filename == '-':
|
||||
if not filename == "-":
|
||||
gfile = pythonopen(filename, "w")
|
||||
gfile.write(final)
|
||||
gfile.close()
|
||||
@@ -288,14 +335,32 @@ def parse(pathobj):
|
||||
|
||||
out = ""
|
||||
lastcommand = None
|
||||
precision_string = '.' + str(PRECISION) + 'f'
|
||||
precision_string = "." + str(PRECISION) + "f"
|
||||
currLocation = {} # keep track for no doubles
|
||||
print("Startup!")
|
||||
|
||||
# the order of parameters
|
||||
# arcs need work. original code from mach3_4 doesn't want K properties on XY plane. Not sure
|
||||
# what fanuc does here.
|
||||
params = ['X', 'Y', 'Z', 'A', 'B', 'C', 'I', 'J', 'F', 'S', 'T', 'Q', 'R', 'L', 'H', 'D', 'P']
|
||||
params = [
|
||||
"X",
|
||||
"Y",
|
||||
"Z",
|
||||
"A",
|
||||
"B",
|
||||
"C",
|
||||
"I",
|
||||
"J",
|
||||
"F",
|
||||
"S",
|
||||
"T",
|
||||
"Q",
|
||||
"R",
|
||||
"L",
|
||||
"H",
|
||||
"D",
|
||||
"P",
|
||||
]
|
||||
firstmove = Path.Command("G0", {"X": -1, "Y": -1, "Z": -1, "F": 0.0})
|
||||
currLocation.update(firstmove.Parameters) # set First location Parameters
|
||||
|
||||
@@ -318,33 +383,47 @@ def parse(pathobj):
|
||||
opHorizRapid = 0
|
||||
opVertRapid = 0
|
||||
|
||||
if 'Adaptive' in pathobj.Name:
|
||||
if "Adaptive" in pathobj.Name:
|
||||
adaptiveOp = True
|
||||
if hasattr(pathobj, 'ToolController'):
|
||||
if hasattr(pathobj.ToolController, 'HorizRapid') and pathobj.ToolController.HorizRapid > 0:
|
||||
opHorizRapid = Units.Quantity(pathobj.ToolController.HorizRapid, FreeCAD.Units.Velocity)
|
||||
if hasattr(pathobj, "ToolController"):
|
||||
if (
|
||||
hasattr(pathobj.ToolController, "HorizRapid")
|
||||
and pathobj.ToolController.HorizRapid > 0
|
||||
):
|
||||
opHorizRapid = Units.Quantity(
|
||||
pathobj.ToolController.HorizRapid, FreeCAD.Units.Velocity
|
||||
)
|
||||
else:
|
||||
FreeCAD.Console.PrintWarning('Tool Controller Horizontal Rapid Values are unset'+ '\n')
|
||||
FreeCAD.Console.PrintWarning(
|
||||
"Tool Controller Horizontal Rapid Values are unset" + "\n"
|
||||
)
|
||||
|
||||
if hasattr(pathobj.ToolController, 'VertRapid') and pathobj.ToolController.VertRapid > 0:
|
||||
opVertRapid = Units.Quantity(pathobj.ToolController.VertRapid, FreeCAD.Units.Velocity)
|
||||
if (
|
||||
hasattr(pathobj.ToolController, "VertRapid")
|
||||
and pathobj.ToolController.VertRapid > 0
|
||||
):
|
||||
opVertRapid = Units.Quantity(
|
||||
pathobj.ToolController.VertRapid, FreeCAD.Units.Velocity
|
||||
)
|
||||
else:
|
||||
FreeCAD.Console.PrintWarning('Tool Controller Vertical Rapid Values are unset'+ '\n')
|
||||
FreeCAD.Console.PrintWarning(
|
||||
"Tool Controller Vertical Rapid Values are unset" + "\n"
|
||||
)
|
||||
|
||||
for index,c in enumerate(pathobj.Path.Commands):
|
||||
for index, c in enumerate(pathobj.Path.Commands):
|
||||
|
||||
outstring = []
|
||||
command = c.Name
|
||||
if index+1 == len(pathobj.Path.Commands):
|
||||
nextcommand = ""
|
||||
if index + 1 == len(pathobj.Path.Commands):
|
||||
nextcommand = ""
|
||||
else:
|
||||
nextcommand = pathobj.Path.Commands[index+1].Name
|
||||
nextcommand = pathobj.Path.Commands[index + 1].Name
|
||||
|
||||
if adaptiveOp and c.Name in ["G0", "G00"]:
|
||||
if opHorizRapid and opVertRapid:
|
||||
command = 'G1'
|
||||
command = "G1"
|
||||
else:
|
||||
outstring.append('(TOOL CONTROLLER RAPID VALUES ARE UNSET)' + '\n')
|
||||
outstring.append("(TOOL CONTROLLER RAPID VALUES ARE UNSET)" + "\n")
|
||||
|
||||
# suppress moves in fixture selection
|
||||
if pathobj.Label == "Fixture":
|
||||
@@ -359,45 +438,82 @@ def parse(pathobj):
|
||||
|
||||
# convert drill cycles to tap cycles if tool is a tap
|
||||
if command == "G81" or command == "G83":
|
||||
if hasattr(pathobj, 'ToolController') and pathobj.ToolController.Tool.ToolType == "Tap":
|
||||
if (
|
||||
hasattr(pathobj, "ToolController")
|
||||
and pathobj.ToolController.Tool.ToolType == "Tap"
|
||||
):
|
||||
command = "G84"
|
||||
out += linenumber() + "G95\n"
|
||||
paramstring = ""
|
||||
for param in [ "X", "Y" ]:
|
||||
for param in ["X", "Y"]:
|
||||
if param in c.Parameters:
|
||||
if (not OUTPUT_DOUBLES) and (param in currLocation) and (currLocation[param] == c.Parameters[param]):
|
||||
if (
|
||||
(not OUTPUT_DOUBLES)
|
||||
and (param in currLocation)
|
||||
and (currLocation[param] == c.Parameters[param])
|
||||
):
|
||||
continue
|
||||
else:
|
||||
pos = Units.Quantity(c.Parameters[param], FreeCAD.Units.Length)
|
||||
paramstring += " " + param + format(float(pos.getValueAs(UNIT_FORMAT)), precision_string)
|
||||
pos = Units.Quantity(
|
||||
c.Parameters[param], FreeCAD.Units.Length
|
||||
)
|
||||
paramstring += (
|
||||
" "
|
||||
+ param
|
||||
+ format(
|
||||
float(pos.getValueAs(UNIT_FORMAT)),
|
||||
precision_string,
|
||||
)
|
||||
)
|
||||
if paramstring != "":
|
||||
out += linenumber() + "G00"+paramstring+"\n"
|
||||
out += linenumber() + "G00" + paramstring + "\n"
|
||||
|
||||
if "S" in c.Parameters:
|
||||
tapSpeed = int(c.Parameters['S'])
|
||||
out += "M29 S"+str(tapSpeed)+"\n"
|
||||
tapSpeed = int(c.Parameters["S"])
|
||||
out += "M29 S" + str(tapSpeed) + "\n"
|
||||
|
||||
for param in [ "Z", "R" ]:
|
||||
for param in ["Z", "R"]:
|
||||
if param in c.Parameters:
|
||||
if (not OUTPUT_DOUBLES) and (param in currLocation) and (currLocation[param] == c.Parameters[param]):
|
||||
if (
|
||||
(not OUTPUT_DOUBLES)
|
||||
and (param in currLocation)
|
||||
and (currLocation[param] == c.Parameters[param])
|
||||
):
|
||||
continue
|
||||
else:
|
||||
pos = Units.Quantity(c.Parameters[param], FreeCAD.Units.Length)
|
||||
paramstring += " " + param + format(float(pos.getValueAs(UNIT_FORMAT)), precision_string)
|
||||
pos = Units.Quantity(
|
||||
c.Parameters[param], FreeCAD.Units.Length
|
||||
)
|
||||
paramstring += (
|
||||
" "
|
||||
+ param
|
||||
+ format(
|
||||
float(pos.getValueAs(UNIT_FORMAT)),
|
||||
precision_string,
|
||||
)
|
||||
)
|
||||
# in this mode, F is the distance per revolution of the thread (pitch)
|
||||
# P is the dwell time in seconds at the bottom of the thread
|
||||
# Q is the peck depth of the threading operation
|
||||
for param in [ "F", "P", "Q" ]:
|
||||
for param in ["F", "P", "Q"]:
|
||||
if param in c.Parameters:
|
||||
value = Units.Quantity(c.Parameters[param], FreeCAD.Units.Length)
|
||||
paramstring += " " + param + format(float(value.getValueAs(UNIT_FORMAT)), precision_string)
|
||||
value = Units.Quantity(
|
||||
c.Parameters[param], FreeCAD.Units.Length
|
||||
)
|
||||
paramstring += (
|
||||
" "
|
||||
+ param
|
||||
+ format(
|
||||
float(value.getValueAs(UNIT_FORMAT)),
|
||||
precision_string,
|
||||
)
|
||||
)
|
||||
|
||||
out += linenumber() + "G84" + paramstring + "\n"
|
||||
out += linenumber() + "G80\n"
|
||||
out += linenumber() + "G94\n"
|
||||
continue
|
||||
|
||||
|
||||
outstring.append(command)
|
||||
|
||||
# if modal: suppress the command if it is the same as the last one
|
||||
@@ -409,48 +525,83 @@ def parse(pathobj):
|
||||
if command == "G80" and lastcommand == nextcommand:
|
||||
continue
|
||||
|
||||
if c.Name[0] == '(' and not OUTPUT_COMMENTS: # command is a comment
|
||||
if c.Name[0] == "(" and not OUTPUT_COMMENTS: # command is a comment
|
||||
continue
|
||||
|
||||
# Now add the remaining parameters in order
|
||||
for param in params:
|
||||
if param in c.Parameters:
|
||||
if param == 'F' and (currLocation[param] != c.Parameters[param] or OUTPUT_DOUBLES):
|
||||
if c.Name not in ["G0", "G00"]: # fanuc doesn't use rapid speeds
|
||||
speed = Units.Quantity(c.Parameters['F'], FreeCAD.Units.Velocity)
|
||||
if param == "F" and (
|
||||
currLocation[param] != c.Parameters[param] or OUTPUT_DOUBLES
|
||||
):
|
||||
if c.Name not in [
|
||||
"G0",
|
||||
"G00",
|
||||
]: # fanuc doesn't use rapid speeds
|
||||
speed = Units.Quantity(
|
||||
c.Parameters["F"], FreeCAD.Units.Velocity
|
||||
)
|
||||
if speed.getValueAs(UNIT_SPEED_FORMAT) > 0.0:
|
||||
outstring.append(param + format(float(speed.getValueAs(UNIT_SPEED_FORMAT)), precision_string))
|
||||
outstring.append(
|
||||
param
|
||||
+ format(
|
||||
float(speed.getValueAs(UNIT_SPEED_FORMAT)),
|
||||
precision_string,
|
||||
)
|
||||
)
|
||||
else:
|
||||
continue
|
||||
elif param == 'T':
|
||||
outstring.append(param + str(int(c.Parameters['T'])))
|
||||
elif param == 'H':
|
||||
outstring.append(param + str(int(c.Parameters['H'])))
|
||||
elif param == 'D':
|
||||
outstring.append(param + str(int(c.Parameters['D'])))
|
||||
elif param == 'S':
|
||||
outstring.append(param + str(int(c.Parameters['S'])))
|
||||
elif param == "T":
|
||||
outstring.append(param + str(int(c.Parameters["T"])))
|
||||
elif param == "H":
|
||||
outstring.append(param + str(int(c.Parameters["H"])))
|
||||
elif param == "D":
|
||||
outstring.append(param + str(int(c.Parameters["D"])))
|
||||
elif param == "S":
|
||||
outstring.append(param + str(int(c.Parameters["S"])))
|
||||
else:
|
||||
if (not OUTPUT_DOUBLES) and (param in currLocation) and (currLocation[param] == c.Parameters[param]):
|
||||
if (
|
||||
(not OUTPUT_DOUBLES)
|
||||
and (param in currLocation)
|
||||
and (currLocation[param] == c.Parameters[param])
|
||||
):
|
||||
continue
|
||||
else:
|
||||
pos = Units.Quantity(c.Parameters[param], FreeCAD.Units.Length)
|
||||
pos = Units.Quantity(
|
||||
c.Parameters[param], FreeCAD.Units.Length
|
||||
)
|
||||
outstring.append(
|
||||
param + format(float(pos.getValueAs(UNIT_FORMAT)), precision_string))
|
||||
param
|
||||
+ format(
|
||||
float(pos.getValueAs(UNIT_FORMAT)), precision_string
|
||||
)
|
||||
)
|
||||
|
||||
if adaptiveOp and c.Name in ["G0", "G00"]:
|
||||
if opHorizRapid and opVertRapid:
|
||||
if 'Z' not in c.Parameters:
|
||||
outstring.append('F' + format(float(opHorizRapid.getValueAs(UNIT_SPEED_FORMAT)), precision_string))
|
||||
if "Z" not in c.Parameters:
|
||||
outstring.append(
|
||||
"F"
|
||||
+ format(
|
||||
float(opHorizRapid.getValueAs(UNIT_SPEED_FORMAT)),
|
||||
precision_string,
|
||||
)
|
||||
)
|
||||
else:
|
||||
outstring.append('F' + format(float(opVertRapid.getValueAs(UNIT_SPEED_FORMAT)), precision_string))
|
||||
outstring.append(
|
||||
"F"
|
||||
+ format(
|
||||
float(opVertRapid.getValueAs(UNIT_SPEED_FORMAT)),
|
||||
precision_string,
|
||||
)
|
||||
)
|
||||
|
||||
# store the latest command
|
||||
lastcommand = command
|
||||
currLocation.update(c.Parameters)
|
||||
|
||||
# Check for Tool Change:
|
||||
if command == 'M6':
|
||||
if command == "M6":
|
||||
# stop the spindle
|
||||
out += linenumber() + "M5\n"
|
||||
for line in TOOL_CHANGE.splitlines(True):
|
||||
@@ -458,7 +609,7 @@ def parse(pathobj):
|
||||
|
||||
# add height offset
|
||||
if USE_TLO:
|
||||
tool_height = '\nG43 H' + str(int(c.Parameters['T']))
|
||||
tool_height = "\nG43 H" + str(int(c.Parameters["T"]))
|
||||
outstring.append(tool_height)
|
||||
|
||||
if command == "message":
|
||||
@@ -479,4 +630,5 @@ def parse(pathobj):
|
||||
|
||||
return out
|
||||
|
||||
|
||||
# print(__name__ + " gcode postprocessor loaded.")
|
||||
|
||||
@@ -580,7 +580,7 @@ def parse(pathobj):
|
||||
out += linenumber() + format_outstring(outstring) + "\n"
|
||||
|
||||
# Check for comments containing machine-specific commands to pass literally to the controller
|
||||
m = re.match(r'^\(MC_RUN_COMMAND: ([^)]+)\)$', command)
|
||||
m = re.match(r"^\(MC_RUN_COMMAND: ([^)]+)\)$", command)
|
||||
if m:
|
||||
raw_command = m.group(1)
|
||||
out += linenumber() + raw_command + "\n"
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -30,7 +30,7 @@ import datetime
|
||||
import shlex
|
||||
from PathScripts import PostUtils
|
||||
|
||||
TOOLTIP = '''
|
||||
TOOLTIP = """
|
||||
This is a postprocessor file for the Path workbench. It is used to
|
||||
take a pseudo-gcode fragment outputted by a Path object, and output
|
||||
real GCode suitable for a jtech photonics laser. This postprocessor, once placed
|
||||
@@ -39,22 +39,50 @@ FreeCAD, via the GUI importer or via python scripts with:
|
||||
|
||||
import jtech_post
|
||||
jtech_post.export(object,"/path/to/file.ngc","")
|
||||
'''
|
||||
"""
|
||||
|
||||
now = datetime.datetime.now()
|
||||
|
||||
parser = argparse.ArgumentParser(prog='jtech', add_help=False)
|
||||
parser.add_argument('--no-header', action='store_true', help='suppress header output')
|
||||
parser.add_argument('--no-comments', action='store_true', help='suppress comment output')
|
||||
parser.add_argument('--line-numbers', action='store_true', help='prefix with line numbers')
|
||||
parser.add_argument('--no-show-editor', action='store_true', help='don\'t pop up editor before writing output')
|
||||
parser.add_argument('--precision', default='3', help='number of digits of precision, default=3')
|
||||
parser.add_argument('--preamble', help='set commands to be issued before the first command, default="M05 S0\nG90"')
|
||||
parser.add_argument('--postamble', help='set commands to be issued after the last command, default="M05 S0\nM2"')
|
||||
parser.add_argument('--inches', action='store_true', help='Convert output for US imperial mode (G20)')
|
||||
parser.add_argument('--modal', action='store_true', help='Output the Same G-command Name USE NonModal Mode')
|
||||
parser.add_argument('--axis-modal', action='store_true', help='Output the Same Axis Value Mode')
|
||||
parser.add_argument('--power-on-delay', default='255', help='milliseconds - Add a delay after laser on before moving to pre-heat material. Default=0')
|
||||
parser = argparse.ArgumentParser(prog="jtech", add_help=False)
|
||||
parser.add_argument("--no-header", action="store_true", help="suppress header output")
|
||||
parser.add_argument(
|
||||
"--no-comments", action="store_true", help="suppress comment output"
|
||||
)
|
||||
parser.add_argument(
|
||||
"--line-numbers", action="store_true", help="prefix with line numbers"
|
||||
)
|
||||
parser.add_argument(
|
||||
"--no-show-editor",
|
||||
action="store_true",
|
||||
help="don't pop up editor before writing output",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--precision", default="3", help="number of digits of precision, default=3"
|
||||
)
|
||||
parser.add_argument(
|
||||
"--preamble",
|
||||
help='set commands to be issued before the first command, default="M05 S0\nG90"',
|
||||
)
|
||||
parser.add_argument(
|
||||
"--postamble",
|
||||
help='set commands to be issued after the last command, default="M05 S0\nM2"',
|
||||
)
|
||||
parser.add_argument(
|
||||
"--inches", action="store_true", help="Convert output for US imperial mode (G20)"
|
||||
)
|
||||
parser.add_argument(
|
||||
"--modal",
|
||||
action="store_true",
|
||||
help="Output the Same G-command Name USE NonModal Mode",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--axis-modal", action="store_true", help="Output the Same Axis Value Mode"
|
||||
)
|
||||
parser.add_argument(
|
||||
"--power-on-delay",
|
||||
default="255",
|
||||
help="milliseconds - Add a delay after laser on before moving to pre-heat material. Default=0",
|
||||
)
|
||||
|
||||
|
||||
TOOLTIP_ARGS = parser.format_help()
|
||||
@@ -65,48 +93,50 @@ OUTPUT_HEADER = True
|
||||
OUTPUT_LINE_NUMBERS = False
|
||||
SHOW_EDITOR = True
|
||||
MODAL = False # if true commands are suppressed if the same as previous line.
|
||||
OUTPUT_DOUBLES = True # if false duplicate axis values are suppressed if the same as previous line.
|
||||
OUTPUT_DOUBLES = (
|
||||
True # if false duplicate axis values are suppressed if the same as previous line.
|
||||
)
|
||||
COMMAND_SPACE = " "
|
||||
LINENR = 100 # line number starting value
|
||||
|
||||
# These globals will be reflected in the Machine configuration of the project
|
||||
UNITS = "G21" # G21 for metric, G20 for us standard
|
||||
UNIT_SPEED_FORMAT = 'mm/min'
|
||||
UNIT_FORMAT = 'mm'
|
||||
UNIT_SPEED_FORMAT = "mm/min"
|
||||
UNIT_FORMAT = "mm"
|
||||
|
||||
MACHINE_NAME = "JTECH Photonic Laser"
|
||||
PRECISION = 3
|
||||
|
||||
# Preamble text will appear at the beginning of the GCODE output file.
|
||||
PREAMBLE = '''M05 S0
|
||||
PREAMBLE = """M05 S0
|
||||
G90
|
||||
'''
|
||||
"""
|
||||
|
||||
# Postamble text will appear following the last operation.
|
||||
POSTAMBLE = '''M05 S0
|
||||
POSTAMBLE = """M05 S0
|
||||
M2
|
||||
'''
|
||||
"""
|
||||
|
||||
PRE_FEED = '''M03
|
||||
PRE_FEED = """M03
|
||||
G4 P{}
|
||||
'''
|
||||
"""
|
||||
|
||||
POST_FEED = '''M05
|
||||
'''
|
||||
POST_FEED = """M05
|
||||
"""
|
||||
|
||||
# Pre operation text will be inserted before every operation
|
||||
PRE_OPERATION = ''''''
|
||||
PRE_OPERATION = """"""
|
||||
|
||||
# Post operation text will be inserted after every operation
|
||||
POST_OPERATION = ''''''
|
||||
POST_OPERATION = """"""
|
||||
|
||||
# Tool Change commands will be inserted before a tool change
|
||||
TOOL_CHANGE = ''''''
|
||||
TOOL_CHANGE = """"""
|
||||
|
||||
POWER_ON_DELAY = 0
|
||||
|
||||
# to distinguish python built-in open function from the one declared below
|
||||
if open.__module__ == '__builtin__':
|
||||
if open.__module__ == "__builtin__":
|
||||
pythonopen = open
|
||||
|
||||
|
||||
@@ -142,9 +172,9 @@ def processArguments(argstring):
|
||||
if args.postamble is not None:
|
||||
POSTAMBLE = args.postamble
|
||||
if args.inches:
|
||||
UNITS = 'G20'
|
||||
UNIT_SPEED_FORMAT = 'in/min'
|
||||
UNIT_FORMAT = 'in'
|
||||
UNITS = "G20"
|
||||
UNIT_SPEED_FORMAT = "in/min"
|
||||
UNIT_FORMAT = "in"
|
||||
PRECISION = 4
|
||||
if args.modal:
|
||||
MODAL = True
|
||||
@@ -164,7 +194,11 @@ def export(objectslist, filename, argstring):
|
||||
|
||||
for obj in objectslist:
|
||||
if not hasattr(obj, "Path"):
|
||||
print("the object " + obj.Name + " is not a path. Please select only path and Compounds.")
|
||||
print(
|
||||
"the object "
|
||||
+ obj.Name
|
||||
+ " is not a path. Please select only path and Compounds."
|
||||
)
|
||||
return None
|
||||
|
||||
print("postprocessing...")
|
||||
@@ -218,7 +252,7 @@ def export(objectslist, filename, argstring):
|
||||
|
||||
print("done postprocessing.")
|
||||
|
||||
if not filename == '-':
|
||||
if not filename == "-":
|
||||
gfile = pythonopen(filename, "wb")
|
||||
gfile.write(final)
|
||||
gfile.close()
|
||||
@@ -237,12 +271,30 @@ def linenumber():
|
||||
def parse(pathobj):
|
||||
out = ""
|
||||
lastcommand = None
|
||||
precision_string = '.' + str(PRECISION) + 'f'
|
||||
precision_string = "." + str(PRECISION) + "f"
|
||||
currLocation = {} # keep track for no doubles
|
||||
RAPID_MOVES = ["G0", "G00"]
|
||||
FEED_MOVES = ["G1", "G01", "G2", "G02", "G3", "G03"]
|
||||
# the order of parameters
|
||||
params = ['X', 'Y', 'Z', 'A', 'B', 'C', 'I', 'J', 'F', 'S', 'T', 'Q', 'R', 'L', 'H', 'D', 'P']
|
||||
params = [
|
||||
"X",
|
||||
"Y",
|
||||
"Z",
|
||||
"A",
|
||||
"B",
|
||||
"C",
|
||||
"I",
|
||||
"J",
|
||||
"F",
|
||||
"S",
|
||||
"T",
|
||||
"Q",
|
||||
"R",
|
||||
"L",
|
||||
"H",
|
||||
"D",
|
||||
"P",
|
||||
]
|
||||
firstmove = Path.Command("G0", {"X": -1, "Y": -1, "Z": -1, "F": 0.0})
|
||||
currLocation.update(firstmove.Parameters) # set First location Parameters
|
||||
|
||||
@@ -276,41 +328,63 @@ def parse(pathobj):
|
||||
if command == lastcommand:
|
||||
outstring.pop(0)
|
||||
|
||||
if c.Name[0] == '(' and not OUTPUT_COMMENTS: # command is a comment
|
||||
if c.Name[0] == "(" and not OUTPUT_COMMENTS: # command is a comment
|
||||
continue
|
||||
|
||||
# Now add the remaining parameters in order
|
||||
for param in params:
|
||||
if param in c.Parameters:
|
||||
if param == 'F' and (currLocation[param] != c.Parameters[param] or OUTPUT_DOUBLES):
|
||||
if c.Name not in RAPID_MOVES: # linuxcnc doesn't use rapid speeds
|
||||
speed = Units.Quantity(c.Parameters['F'], FreeCAD.Units.Velocity)
|
||||
if param == "F" and (
|
||||
currLocation[param] != c.Parameters[param] or OUTPUT_DOUBLES
|
||||
):
|
||||
if (
|
||||
c.Name not in RAPID_MOVES
|
||||
): # linuxcnc doesn't use rapid speeds
|
||||
speed = Units.Quantity(
|
||||
c.Parameters["F"], FreeCAD.Units.Velocity
|
||||
)
|
||||
if speed.getValueAs(UNIT_SPEED_FORMAT) > 0.0:
|
||||
outstring.append(param + format(float(speed.getValueAs(UNIT_SPEED_FORMAT)), precision_string))
|
||||
outstring.append(
|
||||
param
|
||||
+ format(
|
||||
float(speed.getValueAs(UNIT_SPEED_FORMAT)),
|
||||
precision_string,
|
||||
)
|
||||
)
|
||||
else:
|
||||
continue
|
||||
elif param == 'T':
|
||||
outstring.append(param + str(int(c.Parameters['T'])))
|
||||
elif param == 'H':
|
||||
outstring.append(param + str(int(c.Parameters['H'])))
|
||||
elif param == 'D':
|
||||
outstring.append(param + str(int(c.Parameters['D'])))
|
||||
elif param == 'S':
|
||||
outstring.append(param + str(int(c.Parameters['S'])))
|
||||
elif param == "T":
|
||||
outstring.append(param + str(int(c.Parameters["T"])))
|
||||
elif param == "H":
|
||||
outstring.append(param + str(int(c.Parameters["H"])))
|
||||
elif param == "D":
|
||||
outstring.append(param + str(int(c.Parameters["D"])))
|
||||
elif param == "S":
|
||||
outstring.append(param + str(int(c.Parameters["S"])))
|
||||
else:
|
||||
if (not OUTPUT_DOUBLES) and (param in currLocation) and (currLocation[param] == c.Parameters[param]):
|
||||
if (
|
||||
(not OUTPUT_DOUBLES)
|
||||
and (param in currLocation)
|
||||
and (currLocation[param] == c.Parameters[param])
|
||||
):
|
||||
continue
|
||||
else:
|
||||
pos = Units.Quantity(c.Parameters[param], FreeCAD.Units.Length)
|
||||
pos = Units.Quantity(
|
||||
c.Parameters[param], FreeCAD.Units.Length
|
||||
)
|
||||
outstring.append(
|
||||
param + format(float(pos.getValueAs(UNIT_FORMAT)), precision_string))
|
||||
param
|
||||
+ format(
|
||||
float(pos.getValueAs(UNIT_FORMAT)), precision_string
|
||||
)
|
||||
)
|
||||
|
||||
# store the latest command
|
||||
lastcommand = command
|
||||
currLocation.update(c.Parameters)
|
||||
|
||||
# Check for Tool Change:
|
||||
if command == 'M6':
|
||||
if command == "M6":
|
||||
continue
|
||||
|
||||
if command == "message":
|
||||
|
||||
@@ -30,7 +30,7 @@ import datetime
|
||||
import shlex
|
||||
from PathScripts import PostUtils
|
||||
|
||||
TOOLTIP = '''
|
||||
TOOLTIP = """
|
||||
This is a postprocessor file for the Path workbench. It is used to
|
||||
take a pseudo-gcode fragment outputted by a Path object, and output
|
||||
real GCode suitable for a linuxcnc 3 axis mill. This postprocessor, once placed
|
||||
@@ -39,22 +39,50 @@ FreeCAD, via the GUI importer or via python scripts with:
|
||||
|
||||
import linuxcnc_post
|
||||
linuxcnc_post.export(object,"/path/to/file.ncc","")
|
||||
'''
|
||||
"""
|
||||
|
||||
now = datetime.datetime.now()
|
||||
|
||||
parser = argparse.ArgumentParser(prog='linuxcnc', add_help=False)
|
||||
parser.add_argument('--no-header', action='store_true', help='suppress header output')
|
||||
parser.add_argument('--no-comments', action='store_true', help='suppress comment output')
|
||||
parser.add_argument('--line-numbers', action='store_true', help='prefix with line numbers')
|
||||
parser.add_argument('--no-show-editor', action='store_true', help='don\'t pop up editor before writing output')
|
||||
parser.add_argument('--precision', default='3', help='number of digits of precision, default=3')
|
||||
parser.add_argument('--preamble', help='set commands to be issued before the first command, default="G17\nG90"')
|
||||
parser.add_argument('--postamble', help='set commands to be issued after the last command, default="M05\nG17 G90\nM2"')
|
||||
parser.add_argument('--inches', action='store_true', help='Convert output for US imperial mode (G20)')
|
||||
parser.add_argument('--modal', action='store_true', help='Output the Same G-command Name USE NonModal Mode')
|
||||
parser.add_argument('--axis-modal', action='store_true', help='Output the Same Axis Value Mode')
|
||||
parser.add_argument('--no-tlo', action='store_true', help='suppress tool length offset (G43) following tool changes')
|
||||
parser = argparse.ArgumentParser(prog="linuxcnc", add_help=False)
|
||||
parser.add_argument("--no-header", action="store_true", help="suppress header output")
|
||||
parser.add_argument(
|
||||
"--no-comments", action="store_true", help="suppress comment output"
|
||||
)
|
||||
parser.add_argument(
|
||||
"--line-numbers", action="store_true", help="prefix with line numbers"
|
||||
)
|
||||
parser.add_argument(
|
||||
"--no-show-editor",
|
||||
action="store_true",
|
||||
help="don't pop up editor before writing output",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--precision", default="3", help="number of digits of precision, default=3"
|
||||
)
|
||||
parser.add_argument(
|
||||
"--preamble",
|
||||
help='set commands to be issued before the first command, default="G17\nG90"',
|
||||
)
|
||||
parser.add_argument(
|
||||
"--postamble",
|
||||
help='set commands to be issued after the last command, default="M05\nG17 G90\nM2"',
|
||||
)
|
||||
parser.add_argument(
|
||||
"--inches", action="store_true", help="Convert output for US imperial mode (G20)"
|
||||
)
|
||||
parser.add_argument(
|
||||
"--modal",
|
||||
action="store_true",
|
||||
help="Output the Same G-command Name USE NonModal Mode",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--axis-modal", action="store_true", help="Output the Same Axis Value Mode"
|
||||
)
|
||||
parser.add_argument(
|
||||
"--no-tlo",
|
||||
action="store_true",
|
||||
help="suppress tool length offset (G43) following tool changes",
|
||||
)
|
||||
|
||||
TOOLTIP_ARGS = parser.format_help()
|
||||
|
||||
@@ -64,42 +92,44 @@ OUTPUT_HEADER = True
|
||||
OUTPUT_LINE_NUMBERS = False
|
||||
SHOW_EDITOR = True
|
||||
MODAL = False # if true commands are suppressed if the same as previous line.
|
||||
USE_TLO = True # if true G43 will be output following tool changes
|
||||
OUTPUT_DOUBLES = True # if false duplicate axis values are suppressed if the same as previous line.
|
||||
USE_TLO = True # if true G43 will be output following tool changes
|
||||
OUTPUT_DOUBLES = (
|
||||
True # if false duplicate axis values are suppressed if the same as previous line.
|
||||
)
|
||||
COMMAND_SPACE = " "
|
||||
LINENR = 100 # line number starting value
|
||||
|
||||
# These globals will be reflected in the Machine configuration of the project
|
||||
UNITS = "G21" # G21 for metric, G20 for us standard
|
||||
UNIT_SPEED_FORMAT = 'mm/min'
|
||||
UNIT_FORMAT = 'mm'
|
||||
UNIT_SPEED_FORMAT = "mm/min"
|
||||
UNIT_FORMAT = "mm"
|
||||
|
||||
MACHINE_NAME = "LinuxCNC"
|
||||
CORNER_MIN = {'x': 0, 'y': 0, 'z': 0}
|
||||
CORNER_MAX = {'x': 500, 'y': 300, 'z': 300}
|
||||
CORNER_MIN = {"x": 0, "y": 0, "z": 0}
|
||||
CORNER_MAX = {"x": 500, "y": 300, "z": 300}
|
||||
PRECISION = 3
|
||||
|
||||
# Preamble text will appear at the beginning of the GCODE output file.
|
||||
PREAMBLE = '''G17 G54 G40 G49 G80 G90
|
||||
'''
|
||||
PREAMBLE = """G17 G54 G40 G49 G80 G90
|
||||
"""
|
||||
|
||||
# Postamble text will appear following the last operation.
|
||||
POSTAMBLE = '''M05
|
||||
POSTAMBLE = """M05
|
||||
G17 G54 G90 G80 G40
|
||||
M2
|
||||
'''
|
||||
"""
|
||||
|
||||
# Pre operation text will be inserted before every operation
|
||||
PRE_OPERATION = ''''''
|
||||
PRE_OPERATION = """"""
|
||||
|
||||
# Post operation text will be inserted after every operation
|
||||
POST_OPERATION = ''''''
|
||||
POST_OPERATION = """"""
|
||||
|
||||
# Tool Change commands will be inserted before a tool change
|
||||
TOOL_CHANGE = ''''''
|
||||
TOOL_CHANGE = """"""
|
||||
|
||||
# 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
|
||||
|
||||
|
||||
@@ -135,16 +165,16 @@ def processArguments(argstring):
|
||||
if args.postamble is not None:
|
||||
POSTAMBLE = args.postamble
|
||||
if args.inches:
|
||||
UNITS = 'G20'
|
||||
UNIT_SPEED_FORMAT = 'in/min'
|
||||
UNIT_FORMAT = 'in'
|
||||
UNITS = "G20"
|
||||
UNIT_SPEED_FORMAT = "in/min"
|
||||
UNIT_FORMAT = "in"
|
||||
PRECISION = 4
|
||||
if args.modal:
|
||||
MODAL = True
|
||||
if args.no_tlo:
|
||||
USE_TLO = False
|
||||
if args.axis_modal:
|
||||
print ('here')
|
||||
print("here")
|
||||
OUTPUT_DOUBLES = False
|
||||
|
||||
except Exception:
|
||||
@@ -152,6 +182,7 @@ def processArguments(argstring):
|
||||
|
||||
return True
|
||||
|
||||
|
||||
def export(objectslist, filename, argstring):
|
||||
if not processArguments(argstring):
|
||||
return None
|
||||
@@ -161,7 +192,11 @@ def export(objectslist, filename, argstring):
|
||||
|
||||
for obj in objectslist:
|
||||
if not hasattr(obj, "Path"):
|
||||
print("the object " + obj.Name + " is not a path. Please select only path and Compounds.")
|
||||
print(
|
||||
"the object "
|
||||
+ obj.Name
|
||||
+ " is not a path. Please select only path and Compounds."
|
||||
)
|
||||
return None
|
||||
|
||||
print("postprocessing...")
|
||||
@@ -183,10 +218,10 @@ def export(objectslist, filename, argstring):
|
||||
for obj in objectslist:
|
||||
|
||||
# Skip inactive operations
|
||||
if hasattr(obj, 'Active'):
|
||||
if hasattr(obj, "Active"):
|
||||
if not obj.Active:
|
||||
continue
|
||||
if hasattr(obj, 'Base') and hasattr(obj.Base, 'Active'):
|
||||
if hasattr(obj, "Base") and hasattr(obj.Base, "Active"):
|
||||
if not obj.Base.Active:
|
||||
continue
|
||||
|
||||
@@ -198,8 +233,12 @@ def export(objectslist, filename, argstring):
|
||||
gcode += linenumber() + line
|
||||
|
||||
# get coolant mode
|
||||
coolantMode = 'None'
|
||||
if hasattr(obj, "CoolantMode") or hasattr(obj, 'Base') and hasattr(obj.Base, "CoolantMode"):
|
||||
coolantMode = "None"
|
||||
if (
|
||||
hasattr(obj, "CoolantMode")
|
||||
or hasattr(obj, "Base")
|
||||
and hasattr(obj.Base, "CoolantMode")
|
||||
):
|
||||
if hasattr(obj, "CoolantMode"):
|
||||
coolantMode = obj.CoolantMode
|
||||
else:
|
||||
@@ -207,12 +246,12 @@ def export(objectslist, filename, argstring):
|
||||
|
||||
# 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"
|
||||
|
||||
# process the operation gcode
|
||||
gcode += parse(obj)
|
||||
@@ -224,10 +263,10 @@ def export(objectslist, filename, argstring):
|
||||
gcode += linenumber() + line
|
||||
|
||||
# turn coolant off if required
|
||||
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_COMMENTS:
|
||||
@@ -250,7 +289,7 @@ def export(objectslist, filename, argstring):
|
||||
|
||||
print("done postprocessing.")
|
||||
|
||||
if not filename == '-':
|
||||
if not filename == "-":
|
||||
gfile = pythonopen(filename, "w")
|
||||
gfile.write(final)
|
||||
gfile.close()
|
||||
@@ -275,12 +314,30 @@ def parse(pathobj):
|
||||
|
||||
out = ""
|
||||
lastcommand = None
|
||||
precision_string = '.' + str(PRECISION) + 'f'
|
||||
precision_string = "." + str(PRECISION) + "f"
|
||||
currLocation = {} # keep track for no doubles
|
||||
|
||||
# the order of parameters
|
||||
# linuxcnc doesn't want K properties on XY plane Arcs need work.
|
||||
params = ['X', 'Y', 'Z', 'A', 'B', 'C', 'I', 'J', 'F', 'S', 'T', 'Q', 'R', 'L', 'H', 'D', 'P']
|
||||
params = [
|
||||
"X",
|
||||
"Y",
|
||||
"Z",
|
||||
"A",
|
||||
"B",
|
||||
"C",
|
||||
"I",
|
||||
"J",
|
||||
"F",
|
||||
"S",
|
||||
"T",
|
||||
"Q",
|
||||
"R",
|
||||
"L",
|
||||
"H",
|
||||
"D",
|
||||
"P",
|
||||
]
|
||||
firstmove = Path.Command("G0", {"X": -1, "Y": -1, "Z": -1, "F": 0.0})
|
||||
currLocation.update(firstmove.Parameters) # set First location Parameters
|
||||
|
||||
@@ -310,41 +367,64 @@ def parse(pathobj):
|
||||
if command == lastcommand:
|
||||
outstring.pop(0)
|
||||
|
||||
if c.Name[0] == '(' and not OUTPUT_COMMENTS: # command is a comment
|
||||
if c.Name[0] == "(" and not OUTPUT_COMMENTS: # command is a comment
|
||||
continue
|
||||
|
||||
# Now add the remaining parameters in order
|
||||
for param in params:
|
||||
if param in c.Parameters:
|
||||
if param == 'F' and (currLocation[param] != c.Parameters[param] or OUTPUT_DOUBLES):
|
||||
if c.Name not in ["G0", "G00"]: # linuxcnc doesn't use rapid speeds
|
||||
speed = Units.Quantity(c.Parameters['F'], FreeCAD.Units.Velocity)
|
||||
if param == "F" and (
|
||||
currLocation[param] != c.Parameters[param] or OUTPUT_DOUBLES
|
||||
):
|
||||
if c.Name not in [
|
||||
"G0",
|
||||
"G00",
|
||||
]: # linuxcnc doesn't use rapid speeds
|
||||
speed = Units.Quantity(
|
||||
c.Parameters["F"], FreeCAD.Units.Velocity
|
||||
)
|
||||
if speed.getValueAs(UNIT_SPEED_FORMAT) > 0.0:
|
||||
outstring.append(param + format(float(speed.getValueAs(UNIT_SPEED_FORMAT)), precision_string))
|
||||
outstring.append(
|
||||
param
|
||||
+ format(
|
||||
float(speed.getValueAs(UNIT_SPEED_FORMAT)),
|
||||
precision_string,
|
||||
)
|
||||
)
|
||||
else:
|
||||
continue
|
||||
elif param == 'T':
|
||||
outstring.append(param + str(int(c.Parameters['T'])))
|
||||
elif param == 'H':
|
||||
outstring.append(param + str(int(c.Parameters['H'])))
|
||||
elif param == 'D':
|
||||
outstring.append(param + str(int(c.Parameters['D'])))
|
||||
elif param == 'S':
|
||||
outstring.append(param + str(int(c.Parameters['S'])))
|
||||
elif param == "T":
|
||||
outstring.append(param + str(int(c.Parameters["T"])))
|
||||
elif param == "H":
|
||||
outstring.append(param + str(int(c.Parameters["H"])))
|
||||
elif param == "D":
|
||||
outstring.append(param + str(int(c.Parameters["D"])))
|
||||
elif param == "S":
|
||||
outstring.append(param + str(int(c.Parameters["S"])))
|
||||
else:
|
||||
if (not OUTPUT_DOUBLES) and (param in currLocation) and (currLocation[param] == c.Parameters[param]):
|
||||
if (
|
||||
(not OUTPUT_DOUBLES)
|
||||
and (param in currLocation)
|
||||
and (currLocation[param] == c.Parameters[param])
|
||||
):
|
||||
continue
|
||||
else:
|
||||
pos = Units.Quantity(c.Parameters[param], FreeCAD.Units.Length)
|
||||
pos = Units.Quantity(
|
||||
c.Parameters[param], FreeCAD.Units.Length
|
||||
)
|
||||
outstring.append(
|
||||
param + format(float(pos.getValueAs(UNIT_FORMAT)), precision_string))
|
||||
param
|
||||
+ format(
|
||||
float(pos.getValueAs(UNIT_FORMAT)), precision_string
|
||||
)
|
||||
)
|
||||
|
||||
# store the latest command
|
||||
lastcommand = command
|
||||
currLocation.update(c.Parameters)
|
||||
|
||||
# Check for Tool Change:
|
||||
if command == 'M6':
|
||||
if command == "M6":
|
||||
# stop the spindle
|
||||
out += linenumber() + "M5\n"
|
||||
for line in TOOL_CHANGE.splitlines(True):
|
||||
@@ -352,7 +432,7 @@ def parse(pathobj):
|
||||
|
||||
# add height offset
|
||||
if USE_TLO:
|
||||
tool_height = '\nG43 H' + str(int(c.Parameters['T']))
|
||||
tool_height = "\nG43 H" + str(int(c.Parameters["T"]))
|
||||
outstring.append(tool_height)
|
||||
|
||||
if command == "message":
|
||||
@@ -375,4 +455,5 @@ def parse(pathobj):
|
||||
|
||||
return out
|
||||
|
||||
|
||||
# print(__name__ + " gcode postprocessor loaded.")
|
||||
|
||||
@@ -29,7 +29,7 @@ import datetime
|
||||
import shlex
|
||||
from PathScripts import PostUtils
|
||||
|
||||
TOOLTIP = '''
|
||||
TOOLTIP = """
|
||||
This is a postprocessor file for the Path workbench. It is used to
|
||||
take a pseudo-gcode fragment outputted by a Path object, and output
|
||||
real GCode suitable for a mach3_4 3 axis mill. This postprocessor, once placed
|
||||
@@ -38,22 +38,50 @@ FreeCAD, via the GUI importer or via python scripts with:
|
||||
|
||||
import mach3_4_post
|
||||
mach3_4_post.export(object,"/path/to/file.ncc","")
|
||||
'''
|
||||
"""
|
||||
|
||||
now = datetime.datetime.now()
|
||||
|
||||
parser = argparse.ArgumentParser(prog='mach3_4', add_help=False)
|
||||
parser.add_argument('--no-header', action='store_true', help='suppress header output')
|
||||
parser.add_argument('--no-comments', action='store_true', help='suppress comment output')
|
||||
parser.add_argument('--line-numbers', action='store_true', help='prefix with line numbers')
|
||||
parser.add_argument('--no-show-editor', action='store_true', help='don\'t pop up editor before writing output')
|
||||
parser.add_argument('--precision', default='3', help='number of digits of precision, default=3')
|
||||
parser.add_argument('--preamble', help='set commands to be issued before the first command, default="G17\nG90"')
|
||||
parser.add_argument('--postamble', help='set commands to be issued after the last command, default="M05\nG17 G90\nM2"')
|
||||
parser.add_argument('--inches', action='store_true', help='Convert output for US imperial mode (G20)')
|
||||
parser.add_argument('--modal', action='store_true', help='Output the Same G-command Name USE NonModal Mode')
|
||||
parser.add_argument('--axis-modal', action='store_true', help='Output the Same Axis Value Mode')
|
||||
parser.add_argument('--no-tlo', action='store_true', help='suppress tool length offset (G43) following tool changes')
|
||||
parser = argparse.ArgumentParser(prog="mach3_4", add_help=False)
|
||||
parser.add_argument("--no-header", action="store_true", help="suppress header output")
|
||||
parser.add_argument(
|
||||
"--no-comments", action="store_true", help="suppress comment output"
|
||||
)
|
||||
parser.add_argument(
|
||||
"--line-numbers", action="store_true", help="prefix with line numbers"
|
||||
)
|
||||
parser.add_argument(
|
||||
"--no-show-editor",
|
||||
action="store_true",
|
||||
help="don't pop up editor before writing output",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--precision", default="3", help="number of digits of precision, default=3"
|
||||
)
|
||||
parser.add_argument(
|
||||
"--preamble",
|
||||
help='set commands to be issued before the first command, default="G17\nG90"',
|
||||
)
|
||||
parser.add_argument(
|
||||
"--postamble",
|
||||
help='set commands to be issued after the last command, default="M05\nG17 G90\nM2"',
|
||||
)
|
||||
parser.add_argument(
|
||||
"--inches", action="store_true", help="Convert output for US imperial mode (G20)"
|
||||
)
|
||||
parser.add_argument(
|
||||
"--modal",
|
||||
action="store_true",
|
||||
help="Output the Same G-command Name USE NonModal Mode",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--axis-modal", action="store_true", help="Output the Same Axis Value Mode"
|
||||
)
|
||||
parser.add_argument(
|
||||
"--no-tlo",
|
||||
action="store_true",
|
||||
help="suppress tool length offset (G43) following tool changes",
|
||||
)
|
||||
|
||||
TOOLTIP_ARGS = parser.format_help()
|
||||
|
||||
@@ -63,42 +91,44 @@ OUTPUT_HEADER = True
|
||||
OUTPUT_LINE_NUMBERS = False
|
||||
SHOW_EDITOR = True
|
||||
MODAL = False # if true commands are suppressed if the same as previous line.
|
||||
USE_TLO = True # if true G43 will be output following tool changes
|
||||
OUTPUT_DOUBLES = True # if false duplicate axis values are suppressed if the same as previous line.
|
||||
USE_TLO = True # if true G43 will be output following tool changes
|
||||
OUTPUT_DOUBLES = (
|
||||
True # if false duplicate axis values are suppressed if the same as previous line.
|
||||
)
|
||||
COMMAND_SPACE = " "
|
||||
LINENR = 100 # line number starting value
|
||||
|
||||
# These globals will be reflected in the Machine configuration of the project
|
||||
UNITS = "G21" # G21 for metric, G20 for us standard
|
||||
UNIT_SPEED_FORMAT = 'mm/min'
|
||||
UNIT_FORMAT = 'mm'
|
||||
UNIT_SPEED_FORMAT = "mm/min"
|
||||
UNIT_FORMAT = "mm"
|
||||
|
||||
MACHINE_NAME = "mach3_4"
|
||||
CORNER_MIN = {'x': 0, 'y': 0, 'z': 0}
|
||||
CORNER_MAX = {'x': 500, 'y': 300, 'z': 300}
|
||||
CORNER_MIN = {"x": 0, "y": 0, "z": 0}
|
||||
CORNER_MAX = {"x": 500, "y": 300, "z": 300}
|
||||
PRECISION = 3
|
||||
|
||||
# Preamble text will appear at the beginning of the GCODE output file.
|
||||
PREAMBLE = '''G17 G54 G40 G49 G80 G90
|
||||
'''
|
||||
PREAMBLE = """G17 G54 G40 G49 G80 G90
|
||||
"""
|
||||
|
||||
# Postamble text will appear following the last operation.
|
||||
POSTAMBLE = '''M05
|
||||
POSTAMBLE = """M05
|
||||
G17 G54 G90 G80 G40
|
||||
M2
|
||||
'''
|
||||
"""
|
||||
|
||||
# Pre operation text will be inserted before every operation
|
||||
PRE_OPERATION = ''''''
|
||||
PRE_OPERATION = """"""
|
||||
|
||||
# Post operation text will be inserted after every operation
|
||||
POST_OPERATION = ''''''
|
||||
POST_OPERATION = """"""
|
||||
|
||||
# Tool Change commands will be inserted before a tool change
|
||||
TOOL_CHANGE = ''''''
|
||||
TOOL_CHANGE = """"""
|
||||
|
||||
# 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
|
||||
|
||||
|
||||
@@ -134,16 +164,16 @@ def processArguments(argstring):
|
||||
if args.postamble is not None:
|
||||
POSTAMBLE = args.postamble
|
||||
if args.inches:
|
||||
UNITS = 'G20'
|
||||
UNIT_SPEED_FORMAT = 'in/min'
|
||||
UNIT_FORMAT = 'in'
|
||||
UNITS = "G20"
|
||||
UNIT_SPEED_FORMAT = "in/min"
|
||||
UNIT_FORMAT = "in"
|
||||
PRECISION = 4
|
||||
if args.modal:
|
||||
MODAL = True
|
||||
if args.no_tlo:
|
||||
USE_TLO = False
|
||||
if args.axis_modal:
|
||||
print ('here')
|
||||
print("here")
|
||||
OUTPUT_DOUBLES = False
|
||||
|
||||
except Exception:
|
||||
@@ -161,7 +191,11 @@ def export(objectslist, filename, argstring):
|
||||
|
||||
for obj in objectslist:
|
||||
if not hasattr(obj, "Path"):
|
||||
print("the object " + obj.Name + " is not a path. Please select only path and Compounds.")
|
||||
print(
|
||||
"the object "
|
||||
+ obj.Name
|
||||
+ " is not a path. Please select only path and Compounds."
|
||||
)
|
||||
return None
|
||||
|
||||
print("postprocessing...")
|
||||
@@ -183,23 +217,30 @@ def export(objectslist, filename, argstring):
|
||||
for obj in objectslist:
|
||||
|
||||
# Skip inactive operations
|
||||
if hasattr(obj, 'Active'):
|
||||
if hasattr(obj, "Active"):
|
||||
if not obj.Active:
|
||||
continue
|
||||
if hasattr(obj, 'Base') and hasattr(obj.Base, 'Active'):
|
||||
if hasattr(obj, "Base") and hasattr(obj.Base, "Active"):
|
||||
if not obj.Base.Active:
|
||||
continue
|
||||
|
||||
# do the pre_op
|
||||
if OUTPUT_COMMENTS:
|
||||
gcode += linenumber() + "(begin operation: %s)\n" % obj.Label
|
||||
gcode += linenumber() + "(machine: %s, %s)\n" % (MACHINE_NAME, UNIT_SPEED_FORMAT)
|
||||
gcode += linenumber() + "(machine: %s, %s)\n" % (
|
||||
MACHINE_NAME,
|
||||
UNIT_SPEED_FORMAT,
|
||||
)
|
||||
for line in PRE_OPERATION.splitlines(True):
|
||||
gcode += linenumber() + line
|
||||
|
||||
# get coolant mode
|
||||
coolantMode = 'None'
|
||||
if hasattr(obj, "CoolantMode") or hasattr(obj, 'Base') and hasattr(obj.Base, "CoolantMode"):
|
||||
coolantMode = "None"
|
||||
if (
|
||||
hasattr(obj, "CoolantMode")
|
||||
or hasattr(obj, "Base")
|
||||
and hasattr(obj.Base, "CoolantMode")
|
||||
):
|
||||
if hasattr(obj, "CoolantMode"):
|
||||
coolantMode = obj.CoolantMode
|
||||
else:
|
||||
@@ -207,12 +248,12 @@ def export(objectslist, filename, argstring):
|
||||
|
||||
# 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"
|
||||
|
||||
# process the operation gcode
|
||||
gcode += parse(obj)
|
||||
@@ -224,10 +265,10 @@ def export(objectslist, filename, argstring):
|
||||
gcode += linenumber() + line
|
||||
|
||||
# turn coolant off if required
|
||||
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_COMMENTS:
|
||||
@@ -248,7 +289,7 @@ def export(objectslist, filename, argstring):
|
||||
|
||||
print("done postprocessing.")
|
||||
|
||||
if not filename == '-':
|
||||
if not filename == "-":
|
||||
gfile = pythonopen(filename, "w")
|
||||
gfile.write(final)
|
||||
gfile.close()
|
||||
@@ -273,12 +314,30 @@ def parse(pathobj):
|
||||
|
||||
out = ""
|
||||
lastcommand = None
|
||||
precision_string = '.' + str(PRECISION) + 'f'
|
||||
precision_string = "." + str(PRECISION) + "f"
|
||||
currLocation = {} # keep track for no doubles
|
||||
|
||||
# the order of parameters
|
||||
# mach3_4 doesn't want K properties on XY plane Arcs need work.
|
||||
params = ['X', 'Y', 'Z', 'A', 'B', 'C', 'I', 'J', 'F', 'S', 'T', 'Q', 'R', 'L', 'H', 'D', 'P']
|
||||
params = [
|
||||
"X",
|
||||
"Y",
|
||||
"Z",
|
||||
"A",
|
||||
"B",
|
||||
"C",
|
||||
"I",
|
||||
"J",
|
||||
"F",
|
||||
"S",
|
||||
"T",
|
||||
"Q",
|
||||
"R",
|
||||
"L",
|
||||
"H",
|
||||
"D",
|
||||
"P",
|
||||
]
|
||||
firstmove = Path.Command("G0", {"X": -1, "Y": -1, "Z": -1, "F": 0.0})
|
||||
currLocation.update(firstmove.Parameters) # set First location Parameters
|
||||
|
||||
@@ -301,18 +360,32 @@ def parse(pathobj):
|
||||
opHorizRapid = 0
|
||||
opVertRapid = 0
|
||||
|
||||
if 'Adaptive' in pathobj.Name:
|
||||
if "Adaptive" in pathobj.Name:
|
||||
adaptiveOp = True
|
||||
if hasattr(pathobj, 'ToolController'):
|
||||
if hasattr(pathobj.ToolController, 'HorizRapid') and pathobj.ToolController.HorizRapid > 0:
|
||||
opHorizRapid = Units.Quantity(pathobj.ToolController.HorizRapid, FreeCAD.Units.Velocity)
|
||||
if hasattr(pathobj, "ToolController"):
|
||||
if (
|
||||
hasattr(pathobj.ToolController, "HorizRapid")
|
||||
and pathobj.ToolController.HorizRapid > 0
|
||||
):
|
||||
opHorizRapid = Units.Quantity(
|
||||
pathobj.ToolController.HorizRapid, FreeCAD.Units.Velocity
|
||||
)
|
||||
else:
|
||||
FreeCAD.Console.PrintWarning('Tool Controller Horizontal Rapid Values are unset'+ '\n')
|
||||
FreeCAD.Console.PrintWarning(
|
||||
"Tool Controller Horizontal Rapid Values are unset" + "\n"
|
||||
)
|
||||
|
||||
if hasattr(pathobj.ToolController, 'VertRapid') and pathobj.ToolController.VertRapid > 0:
|
||||
opVertRapid = Units.Quantity(pathobj.ToolController.VertRapid, FreeCAD.Units.Velocity)
|
||||
if (
|
||||
hasattr(pathobj.ToolController, "VertRapid")
|
||||
and pathobj.ToolController.VertRapid > 0
|
||||
):
|
||||
opVertRapid = Units.Quantity(
|
||||
pathobj.ToolController.VertRapid, FreeCAD.Units.Velocity
|
||||
)
|
||||
else:
|
||||
FreeCAD.Console.PrintWarning('Tool Controller Vertical Rapid Values are unset'+ '\n')
|
||||
FreeCAD.Console.PrintWarning(
|
||||
"Tool Controller Vertical Rapid Values are unset" + "\n"
|
||||
)
|
||||
|
||||
for c in pathobj.Path.Commands:
|
||||
|
||||
@@ -321,10 +394,9 @@ def parse(pathobj):
|
||||
|
||||
if adaptiveOp and c.Name in ["G0", "G00"]:
|
||||
if opHorizRapid and opVertRapid:
|
||||
command = 'G1'
|
||||
command = "G1"
|
||||
else:
|
||||
outstring.append('(Tool Controller Rapid Values are unset)' + '\n')
|
||||
|
||||
outstring.append("(Tool Controller Rapid Values are unset)" + "\n")
|
||||
|
||||
outstring.append(command)
|
||||
|
||||
@@ -333,48 +405,83 @@ def parse(pathobj):
|
||||
if command == lastcommand:
|
||||
outstring.pop(0)
|
||||
|
||||
if c.Name[0] == '(' and not OUTPUT_COMMENTS: # command is a comment
|
||||
if c.Name[0] == "(" and not OUTPUT_COMMENTS: # command is a comment
|
||||
continue
|
||||
|
||||
# Now add the remaining parameters in order
|
||||
for param in params:
|
||||
if param in c.Parameters:
|
||||
if param == 'F' and (currLocation[param] != c.Parameters[param] or OUTPUT_DOUBLES):
|
||||
if c.Name not in ["G0", "G00"]: # mach3_4 doesn't use rapid speeds
|
||||
speed = Units.Quantity(c.Parameters['F'], FreeCAD.Units.Velocity)
|
||||
if param == "F" and (
|
||||
currLocation[param] != c.Parameters[param] or OUTPUT_DOUBLES
|
||||
):
|
||||
if c.Name not in [
|
||||
"G0",
|
||||
"G00",
|
||||
]: # mach3_4 doesn't use rapid speeds
|
||||
speed = Units.Quantity(
|
||||
c.Parameters["F"], FreeCAD.Units.Velocity
|
||||
)
|
||||
if speed.getValueAs(UNIT_SPEED_FORMAT) > 0.0:
|
||||
outstring.append(param + format(float(speed.getValueAs(UNIT_SPEED_FORMAT)), precision_string))
|
||||
outstring.append(
|
||||
param
|
||||
+ format(
|
||||
float(speed.getValueAs(UNIT_SPEED_FORMAT)),
|
||||
precision_string,
|
||||
)
|
||||
)
|
||||
else:
|
||||
continue
|
||||
elif param == 'T':
|
||||
outstring.append(param + str(int(c.Parameters['T'])))
|
||||
elif param == 'H':
|
||||
outstring.append(param + str(int(c.Parameters['H'])))
|
||||
elif param == 'D':
|
||||
outstring.append(param + str(int(c.Parameters['D'])))
|
||||
elif param == 'S':
|
||||
outstring.append(param + str(int(c.Parameters['S'])))
|
||||
elif param == "T":
|
||||
outstring.append(param + str(int(c.Parameters["T"])))
|
||||
elif param == "H":
|
||||
outstring.append(param + str(int(c.Parameters["H"])))
|
||||
elif param == "D":
|
||||
outstring.append(param + str(int(c.Parameters["D"])))
|
||||
elif param == "S":
|
||||
outstring.append(param + str(int(c.Parameters["S"])))
|
||||
else:
|
||||
if (not OUTPUT_DOUBLES) and (param in currLocation) and (currLocation[param] == c.Parameters[param]):
|
||||
if (
|
||||
(not OUTPUT_DOUBLES)
|
||||
and (param in currLocation)
|
||||
and (currLocation[param] == c.Parameters[param])
|
||||
):
|
||||
continue
|
||||
else:
|
||||
pos = Units.Quantity(c.Parameters[param], FreeCAD.Units.Length)
|
||||
pos = Units.Quantity(
|
||||
c.Parameters[param], FreeCAD.Units.Length
|
||||
)
|
||||
outstring.append(
|
||||
param + format(float(pos.getValueAs(UNIT_FORMAT)), precision_string))
|
||||
param
|
||||
+ format(
|
||||
float(pos.getValueAs(UNIT_FORMAT)), precision_string
|
||||
)
|
||||
)
|
||||
|
||||
if adaptiveOp and c.Name in ["G0", "G00"]:
|
||||
if opHorizRapid and opVertRapid:
|
||||
if 'Z' not in c.Parameters:
|
||||
outstring.append('F' + format(float(opHorizRapid.getValueAs(UNIT_SPEED_FORMAT)), precision_string))
|
||||
if "Z" not in c.Parameters:
|
||||
outstring.append(
|
||||
"F"
|
||||
+ format(
|
||||
float(opHorizRapid.getValueAs(UNIT_SPEED_FORMAT)),
|
||||
precision_string,
|
||||
)
|
||||
)
|
||||
else:
|
||||
outstring.append('F' + format(float(opVertRapid.getValueAs(UNIT_SPEED_FORMAT)), precision_string))
|
||||
outstring.append(
|
||||
"F"
|
||||
+ format(
|
||||
float(opVertRapid.getValueAs(UNIT_SPEED_FORMAT)),
|
||||
precision_string,
|
||||
)
|
||||
)
|
||||
|
||||
# store the latest command
|
||||
lastcommand = command
|
||||
currLocation.update(c.Parameters)
|
||||
|
||||
# Check for Tool Change:
|
||||
if command == 'M6':
|
||||
if command == "M6":
|
||||
# stop the spindle
|
||||
out += linenumber() + "M5\n"
|
||||
for line in TOOL_CHANGE.splitlines(True):
|
||||
@@ -382,7 +489,7 @@ def parse(pathobj):
|
||||
|
||||
# add height offset
|
||||
if USE_TLO:
|
||||
tool_height = '\nG43 H' + str(int(c.Parameters['T']))
|
||||
tool_height = "\nG43 H" + str(int(c.Parameters["T"]))
|
||||
outstring.append(tool_height)
|
||||
|
||||
if command == "message":
|
||||
@@ -403,4 +510,5 @@ def parse(pathobj):
|
||||
|
||||
return out
|
||||
|
||||
|
||||
# print(__name__ + " gcode postprocessor loaded.")
|
||||
|
||||
@@ -26,7 +26,7 @@ from PathScripts import PostUtils
|
||||
import datetime
|
||||
|
||||
|
||||
TOOLTIP = '''
|
||||
TOOLTIP = """
|
||||
This is a postprocessor file for the Path workbench. It is used to take
|
||||
a pseudo-gcode fragment output by a Path object and output real GCode
|
||||
suitable for the Max Computer GmbH nccad9 Computer Numeric Control.
|
||||
@@ -41,33 +41,35 @@ Supported features:
|
||||
|
||||
import nccad_post
|
||||
nccad_post.export([object], "/path/to/file.knc", "")
|
||||
'''
|
||||
"""
|
||||
|
||||
|
||||
MACHINE_NAME = '''Max Computer GmbH nccad9 MCS/KOSY'''
|
||||
MACHINE_NAME = """Max Computer GmbH nccad9 MCS/KOSY"""
|
||||
|
||||
|
||||
# gCode for changing tools
|
||||
# M01 <String> ; Displays <String> and waits for user interaction
|
||||
TOOL_CHANGE = '''G77 ; Move to release position
|
||||
TOOL_CHANGE = """G77 ; Move to release position
|
||||
M10 O6.0 ; Stop spindle
|
||||
M01 Insert tool TOOL
|
||||
G76 ; Move to reference point to ensure correct coordinates after tool change
|
||||
M10 O6.1 ; Start spindle'''
|
||||
M10 O6.1 ; Start spindle"""
|
||||
|
||||
|
||||
# gCode finishing the program
|
||||
POSTAMBLE = '''G77 ; Move to release position
|
||||
M10 O6.0 ; Stop spindle'''
|
||||
POSTAMBLE = """G77 ; Move to release position
|
||||
M10 O6.0 ; Stop spindle"""
|
||||
|
||||
|
||||
# gCode header with information about CAD-software, post-processor
|
||||
# and date/time
|
||||
HEADER = ''';Exported by FreeCAD
|
||||
HEADER = """;Exported by FreeCAD
|
||||
;Post Processor: {}
|
||||
;CAM file: {}
|
||||
;Output Time: {}
|
||||
'''.format(__name__, App.ActiveDocument.FileName, str(datetime.datetime.now()))
|
||||
""".format(
|
||||
__name__, App.ActiveDocument.FileName, str(datetime.datetime.now())
|
||||
)
|
||||
|
||||
|
||||
def export(objectslist, filename, argstring):
|
||||
@@ -86,14 +88,15 @@ def export(objectslist, filename, argstring):
|
||||
for obj in objectslist:
|
||||
for command in obj.Path.Commands:
|
||||
# Manipulate tool change commands
|
||||
if 'M6' == command.Name:
|
||||
gcode += TOOL_CHANGE.replace('TOOL',
|
||||
str(int(command.Parameters['T'])))
|
||||
elif 'M3' == command.Name:
|
||||
if "M6" == command.Name:
|
||||
gcode += TOOL_CHANGE.replace("TOOL", str(int(command.Parameters["T"])))
|
||||
elif "M3" == command.Name:
|
||||
# Convert spindle speed (rpm) command to comment
|
||||
gcode += ('M01 Set spindle speed to '
|
||||
+ str(int(command.Parameters['S']))
|
||||
+ ' rounds per minute')
|
||||
gcode += (
|
||||
"M01 Set spindle speed to "
|
||||
+ str(int(command.Parameters["S"]))
|
||||
+ " rounds per minute"
|
||||
)
|
||||
else:
|
||||
# Add other commands
|
||||
gcode += command.Name
|
||||
@@ -102,15 +105,15 @@ def export(objectslist, filename, argstring):
|
||||
for parameter, value in command.Parameters.items():
|
||||
# Multiply F parameter value by 10,
|
||||
# FreeCAD = mm/s, nccad = 1/10 mm/s
|
||||
if 'F' == parameter:
|
||||
if "F" == parameter:
|
||||
value *= 10
|
||||
# Add command parameters and values and round float
|
||||
# as nccad9 does not support exponents
|
||||
gcode += ' ' + parameter + str(round(value, 5))
|
||||
gcode += " " + parameter + str(round(value, 5))
|
||||
|
||||
gcode += '\n'
|
||||
gcode += "\n"
|
||||
|
||||
gcode += POSTAMBLE + '\n'
|
||||
gcode += POSTAMBLE + "\n"
|
||||
|
||||
# Open editor window
|
||||
if App.GuiUp:
|
||||
@@ -121,7 +124,7 @@ def export(objectslist, filename, argstring):
|
||||
gcode = dia.editor.toPlainText()
|
||||
|
||||
# Save to file
|
||||
if filename != '-':
|
||||
if filename != "-":
|
||||
gfile = open(filename, "w")
|
||||
gfile.write(gcode)
|
||||
gfile.close()
|
||||
|
||||
@@ -26,7 +26,7 @@ import datetime
|
||||
from PathScripts import PostUtils
|
||||
|
||||
|
||||
TOOLTIP = '''
|
||||
TOOLTIP = """
|
||||
This is an postprocessor file for the Path workbench. It will output path data
|
||||
in a format suitable for OpenSBP controllers like shopbot. This postprocessor,
|
||||
once placed in the appropriate PathScripts folder, can be used directly from
|
||||
@@ -34,9 +34,9 @@ inside FreeCAD, via the GUI importer or via python scripts with:
|
||||
|
||||
import Path
|
||||
Path.write(object,"/path/to/file.ncc","post_opensbp")
|
||||
'''
|
||||
"""
|
||||
|
||||
'''
|
||||
"""
|
||||
DONE:
|
||||
uses native commands
|
||||
handles feed and jog moves
|
||||
@@ -48,15 +48,15 @@ ToDo
|
||||
drilling. Haven't looked at it.
|
||||
many other things
|
||||
|
||||
'''
|
||||
"""
|
||||
|
||||
TOOLTIP_ARGS = '''
|
||||
TOOLTIP_ARGS = """
|
||||
Arguments for opensbp:
|
||||
--comments ... insert comments - mostly for debugging
|
||||
--inches ... convert output to inches
|
||||
--no-header ... suppress header output
|
||||
--no-show-editor ... don't show editor, just save result
|
||||
'''
|
||||
"""
|
||||
|
||||
now = datetime.datetime.now()
|
||||
|
||||
@@ -66,21 +66,21 @@ SHOW_EDITOR = True
|
||||
COMMAND_SPACE = ","
|
||||
|
||||
# Preamble text will appear at the beginning of the GCODE output file.
|
||||
PREAMBLE = ''''''
|
||||
PREAMBLE = """"""
|
||||
# Postamble text will appear following the last operation.
|
||||
POSTAMBLE = ''''''
|
||||
POSTAMBLE = """"""
|
||||
|
||||
# Pre operation text will be inserted before every operation
|
||||
PRE_OPERATION = ''''''
|
||||
PRE_OPERATION = """"""
|
||||
|
||||
# Post operation text will be inserted after every operation
|
||||
POST_OPERATION = ''''''
|
||||
POST_OPERATION = """"""
|
||||
|
||||
# Tool Change commands will be inserted before a tool change
|
||||
TOOL_CHANGE = ''''''
|
||||
TOOL_CHANGE = """"""
|
||||
|
||||
# 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
|
||||
|
||||
CurrentState = {}
|
||||
@@ -105,13 +105,13 @@ def export(objectslist, filename, argstring):
|
||||
global GetValue
|
||||
|
||||
for arg in argstring.split():
|
||||
if arg == '--comments':
|
||||
if arg == "--comments":
|
||||
OUTPUT_COMMENTS = True
|
||||
if arg == '--inches':
|
||||
if arg == "--inches":
|
||||
GetValue = getImperialValue
|
||||
if arg == '--no-header':
|
||||
if arg == "--no-header":
|
||||
OUTPUT_HEADER = False
|
||||
if arg == '--no-show-editor':
|
||||
if arg == "--no-show-editor":
|
||||
SHOW_EDITOR = False
|
||||
|
||||
for obj in objectslist:
|
||||
@@ -122,8 +122,15 @@ def export(objectslist, filename, argstring):
|
||||
return
|
||||
|
||||
CurrentState = {
|
||||
'X': 0, 'Y': 0, 'Z': 0, 'F': 0, 'S': 0,
|
||||
'JSXY': 0, 'JSZ': 0, 'MSXY': 0, 'MSZ': 0
|
||||
"X": 0,
|
||||
"Y": 0,
|
||||
"Z": 0,
|
||||
"F": 0,
|
||||
"S": 0,
|
||||
"JSXY": 0,
|
||||
"JSZ": 0,
|
||||
"MSXY": 0,
|
||||
"MSZ": 0,
|
||||
}
|
||||
print("postprocessing...")
|
||||
gcode = ""
|
||||
@@ -188,26 +195,26 @@ def move(command):
|
||||
# txt += feedrate(command)
|
||||
|
||||
axis = ""
|
||||
for p in ['X', 'Y', 'Z']:
|
||||
for p in ["X", "Y", "Z"]:
|
||||
if p in command.Parameters:
|
||||
if command.Parameters[p] != CurrentState[p]:
|
||||
axis += p
|
||||
|
||||
if 'F' in command.Parameters:
|
||||
speed = command.Parameters['F']
|
||||
if command.Name in ['G1', 'G01']: # move
|
||||
if "F" in command.Parameters:
|
||||
speed = command.Parameters["F"]
|
||||
if command.Name in ["G1", "G01"]: # move
|
||||
movetype = "MS"
|
||||
else: # jog
|
||||
movetype = "JS"
|
||||
zspeed = ""
|
||||
xyspeed = ""
|
||||
if 'Z' in axis:
|
||||
if "Z" in axis:
|
||||
speedKey = "{}Z".format(movetype)
|
||||
speedVal = GetValue(speed)
|
||||
if CurrentState[speedKey] != speedVal:
|
||||
CurrentState[speedKey] = speedVal
|
||||
zspeed = "{:f}".format(speedVal)
|
||||
if ('X' in axis) or ('Y' in axis):
|
||||
if ("X" in axis) or ("Y" in axis):
|
||||
speedKey = "{}XY".format(movetype)
|
||||
speedVal = GetValue(speed)
|
||||
if CurrentState[speedKey] != speedVal:
|
||||
@@ -216,45 +223,45 @@ def move(command):
|
||||
if zspeed or xyspeed:
|
||||
txt += "{},{},{}\n".format(movetype, xyspeed, zspeed)
|
||||
|
||||
if command.Name in ['G0', 'G00']:
|
||||
if command.Name in ["G0", "G00"]:
|
||||
pref = "J"
|
||||
else:
|
||||
pref = "M"
|
||||
|
||||
if axis == "X":
|
||||
txt += pref + "X"
|
||||
txt += "," + format(GetValue(command.Parameters["X"]), '.4f')
|
||||
txt += "," + format(GetValue(command.Parameters["X"]), ".4f")
|
||||
txt += "\n"
|
||||
elif axis == "Y":
|
||||
txt += pref + "Y"
|
||||
txt += "," + format(GetValue(command.Parameters["Y"]), '.4f')
|
||||
txt += "," + format(GetValue(command.Parameters["Y"]), ".4f")
|
||||
txt += "\n"
|
||||
elif axis == "Z":
|
||||
txt += pref + "Z"
|
||||
txt += "," + format(GetValue(command.Parameters["Z"]), '.4f')
|
||||
txt += "," + format(GetValue(command.Parameters["Z"]), ".4f")
|
||||
txt += "\n"
|
||||
elif axis == "XY":
|
||||
txt += pref + "2"
|
||||
txt += "," + format(GetValue(command.Parameters["X"]), '.4f')
|
||||
txt += "," + format(GetValue(command.Parameters["Y"]), '.4f')
|
||||
txt += "," + format(GetValue(command.Parameters["X"]), ".4f")
|
||||
txt += "," + format(GetValue(command.Parameters["Y"]), ".4f")
|
||||
txt += "\n"
|
||||
elif axis == "XZ":
|
||||
txt += pref + "3"
|
||||
txt += "," + format(GetValue(command.Parameters["X"]), '.4f')
|
||||
txt += "," + format(GetValue(command.Parameters["X"]), ".4f")
|
||||
txt += ","
|
||||
txt += "," + format(GetValue(command.Parameters["Z"]), '.4f')
|
||||
txt += "," + format(GetValue(command.Parameters["Z"]), ".4f")
|
||||
txt += "\n"
|
||||
elif axis == "XYZ":
|
||||
txt += pref + "3"
|
||||
txt += "," + format(GetValue(command.Parameters["X"]), '.4f')
|
||||
txt += "," + format(GetValue(command.Parameters["Y"]), '.4f')
|
||||
txt += "," + format(GetValue(command.Parameters["Z"]), '.4f')
|
||||
txt += "," + format(GetValue(command.Parameters["X"]), ".4f")
|
||||
txt += "," + format(GetValue(command.Parameters["Y"]), ".4f")
|
||||
txt += "," + format(GetValue(command.Parameters["Z"]), ".4f")
|
||||
txt += "\n"
|
||||
elif axis == "YZ":
|
||||
txt += pref + "3"
|
||||
txt += ","
|
||||
txt += "," + format(GetValue(command.Parameters["Y"]), '.4f')
|
||||
txt += "," + format(GetValue(command.Parameters["Z"]), '.4f')
|
||||
txt += "," + format(GetValue(command.Parameters["Y"]), ".4f")
|
||||
txt += "," + format(GetValue(command.Parameters["Z"]), ".4f")
|
||||
txt += "\n"
|
||||
elif axis == "":
|
||||
print("warning: skipping duplicate move.")
|
||||
@@ -267,15 +274,15 @@ def move(command):
|
||||
|
||||
|
||||
def arc(command):
|
||||
if command.Name == 'G2': # CW
|
||||
if command.Name == "G2": # CW
|
||||
dirstring = "1"
|
||||
else: # G3 means CCW
|
||||
dirstring = "-1"
|
||||
txt = "CG,,"
|
||||
txt += format(GetValue(command.Parameters['X']), '.4f') + ","
|
||||
txt += format(GetValue(command.Parameters['Y']), '.4f') + ","
|
||||
txt += format(GetValue(command.Parameters['I']), '.4f') + ","
|
||||
txt += format(GetValue(command.Parameters['J']), '.4f') + ","
|
||||
txt += format(GetValue(command.Parameters["X"]), ".4f") + ","
|
||||
txt += format(GetValue(command.Parameters["Y"]), ".4f") + ","
|
||||
txt += format(GetValue(command.Parameters["I"]), ".4f") + ","
|
||||
txt += format(GetValue(command.Parameters["J"]), ".4f") + ","
|
||||
txt += "T" + ","
|
||||
txt += dirstring
|
||||
txt += "\n"
|
||||
@@ -288,9 +295,9 @@ def tool_change(command):
|
||||
txt += "'a tool change happens now\n"
|
||||
for line in TOOL_CHANGE.splitlines(True):
|
||||
txt += line
|
||||
txt += "&ToolName=" + str(int(command.Parameters['T']))
|
||||
txt += "&ToolName=" + str(int(command.Parameters["T"]))
|
||||
txt += "\n"
|
||||
txt += "&Tool=" + str(int(command.Parameters['T']))
|
||||
txt += "&Tool=" + str(int(command.Parameters["T"]))
|
||||
txt += "\n"
|
||||
return txt
|
||||
|
||||
@@ -306,7 +313,7 @@ def spindle(command):
|
||||
pass
|
||||
else:
|
||||
pass
|
||||
txt += "TR," + str(command.Parameters['S']) + "\n"
|
||||
txt += "TR," + str(command.Parameters["S"]) + "\n"
|
||||
txt += "C6\n"
|
||||
txt += "PAUSE 2\n"
|
||||
return txt
|
||||
@@ -326,7 +333,7 @@ scommands = {
|
||||
"G03": arc,
|
||||
"M06": tool_change,
|
||||
"M03": spindle,
|
||||
"message": comment
|
||||
"message": comment,
|
||||
}
|
||||
|
||||
|
||||
@@ -351,10 +358,10 @@ def parse(pathobj):
|
||||
output += scommands[command](c)
|
||||
if c.Parameters:
|
||||
CurrentState.update(c.Parameters)
|
||||
elif command[0] == '(':
|
||||
elif command[0] == "(":
|
||||
output += "' " + command + "\n"
|
||||
else:
|
||||
print("I don't know what the hell the command: ", end='')
|
||||
print("I don't know what the hell the command: ", end="")
|
||||
print(command + " means. Maybe I should support it.")
|
||||
return output
|
||||
|
||||
|
||||
@@ -22,7 +22,7 @@
|
||||
# * *
|
||||
# ***************************************************************************
|
||||
|
||||
'''
|
||||
"""
|
||||
This is a preprocessor file for the Path workbench. Its aim is to
|
||||
parse the contents of a given OpenSBP file, and transform it to make it
|
||||
suitable for use in a Path object. This preprocessor, once placed in the
|
||||
@@ -46,18 +46,24 @@ if operations are preceded by a comment ('New Path ...) They are split into mul
|
||||
TODO
|
||||
Many other OpenSBP commands not handled
|
||||
|
||||
'''
|
||||
"""
|
||||
from __future__ import print_function
|
||||
import FreeCAD
|
||||
import PathScripts.PathUtil as PathUtil
|
||||
import os
|
||||
import Path
|
||||
|
||||
AXIS = 'X', 'Y', 'Z', 'A', 'B' # OpenSBP always puts multiaxis move parameters in this order
|
||||
SPEEDS = 'XY', 'Z', 'A', 'B'
|
||||
AXIS = (
|
||||
"X",
|
||||
"Y",
|
||||
"Z",
|
||||
"A",
|
||||
"B",
|
||||
) # OpenSBP always puts multiaxis move parameters in this order
|
||||
SPEEDS = "XY", "Z", "A", "B"
|
||||
|
||||
# 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
|
||||
|
||||
|
||||
@@ -69,9 +75,9 @@ def open(filename):
|
||||
|
||||
|
||||
def insert(filename, docname):
|
||||
'''called when freecad imports a file
|
||||
"""called when freecad imports a file
|
||||
This insert expects parse to return a list of strings
|
||||
each string will become a separate path'''
|
||||
each string will become a separate path"""
|
||||
gfile = pythonopen(filename)
|
||||
gcode = gfile.read()
|
||||
gfile.close()
|
||||
@@ -90,10 +96,20 @@ def parse(inputstring):
|
||||
lines = inputstring.split("\n")
|
||||
return_output = []
|
||||
output = ""
|
||||
last = {'X': None, 'Y': None, 'Z': None, 'A': None, 'B': None}
|
||||
lastrapidspeed = {'XY': "50", 'Z': "50", 'A': "50", 'B': "50"} # set default rapid speeds
|
||||
lastfeedspeed = {'XY': "50", 'Z': "50", 'A': "50", 'B': "50"} # set default feed speed
|
||||
movecommand = ['G1', 'G0', 'G02', 'G03']
|
||||
last = {"X": None, "Y": None, "Z": None, "A": None, "B": None}
|
||||
lastrapidspeed = {
|
||||
"XY": "50",
|
||||
"Z": "50",
|
||||
"A": "50",
|
||||
"B": "50",
|
||||
} # set default rapid speeds
|
||||
lastfeedspeed = {
|
||||
"XY": "50",
|
||||
"Z": "50",
|
||||
"A": "50",
|
||||
"B": "50",
|
||||
} # set default feed speed
|
||||
movecommand = ["G1", "G0", "G02", "G03"]
|
||||
|
||||
for line in lines:
|
||||
# remove any leftover trailing and preceding spaces
|
||||
@@ -105,22 +121,33 @@ def parse(inputstring):
|
||||
# discard comment and other non strictly gcode lines
|
||||
if line[0:9] == "'New Path":
|
||||
# starting new path
|
||||
if any(x in output for x in movecommand): # make sure the path has at least one move command.
|
||||
if any(
|
||||
x in output for x in movecommand
|
||||
): # make sure the path has at least one move command.
|
||||
return_output.append(output)
|
||||
output = ""
|
||||
continue
|
||||
|
||||
words = [a.strip() for a in line.split(",")]
|
||||
words[0] = words[0].upper()
|
||||
if words[0] in ["J2", "J3", "J4", "J5", "M2", "M3", "M4", "M5"]: # multi-axis jogs and moves
|
||||
if words[0][0] == 'J': # jog move
|
||||
if words[0] in [
|
||||
"J2",
|
||||
"J3",
|
||||
"J4",
|
||||
"J5",
|
||||
"M2",
|
||||
"M3",
|
||||
"M4",
|
||||
"M5",
|
||||
]: # multi-axis jogs and moves
|
||||
if words[0][0] == "J": # jog move
|
||||
s = "G0 "
|
||||
else: # feed move
|
||||
else: # feed move
|
||||
s = "G1 "
|
||||
speed = lastfeedspeed["XY"]
|
||||
|
||||
for i in range(1, len(words)):
|
||||
if words[i] == '':
|
||||
if words[i] == "":
|
||||
if last[AXIS[i - 1]] is None:
|
||||
continue
|
||||
else:
|
||||
@@ -128,19 +155,30 @@ def parse(inputstring):
|
||||
else:
|
||||
s += AXIS[i - 1] + words[i]
|
||||
last[AXIS[i - 1]] = words[i]
|
||||
output += s + " F" + speed + '\n'
|
||||
output += s + " F" + speed + "\n"
|
||||
|
||||
if words[0] in ["JA", "JB", "JX", "JY", "JZ", "MA", "MB", "MX", "MY", "MZ"]: # single axis jogs and moves
|
||||
if words[0][0] == 'J': # jog move
|
||||
if words[0] in [
|
||||
"JA",
|
||||
"JB",
|
||||
"JX",
|
||||
"JY",
|
||||
"JZ",
|
||||
"MA",
|
||||
"MB",
|
||||
"MX",
|
||||
"MY",
|
||||
"MZ",
|
||||
]: # single axis jogs and moves
|
||||
if words[0][0] == "J": # jog move
|
||||
s = "G0 "
|
||||
if words[0][1] in ['X', 'Y']:
|
||||
if words[0][1] in ["X", "Y"]:
|
||||
speed = lastrapidspeed["XY"]
|
||||
else:
|
||||
speed = lastrapidspeed[words[0][1]]
|
||||
|
||||
else: # feed move
|
||||
else: # feed move
|
||||
s = "G1 "
|
||||
if words[0][1] in ['X', 'Y']:
|
||||
if words[0][1] in ["X", "Y"]:
|
||||
speed = lastfeedspeed["XY"]
|
||||
else:
|
||||
speed = lastfeedspeed[words[0][1]]
|
||||
@@ -153,7 +191,7 @@ def parse(inputstring):
|
||||
|
||||
if words[0] in ["JS"]: # set jog speed
|
||||
for i in range(1, len(words)):
|
||||
if words[i] == '':
|
||||
if words[i] == "":
|
||||
continue
|
||||
else:
|
||||
lastrapidspeed[SPEEDS[i - 1]] = words[i]
|
||||
@@ -166,7 +204,7 @@ def parse(inputstring):
|
||||
continue
|
||||
if words[0] in ["MS"]: # set move speed
|
||||
for i in range(1, len(words)):
|
||||
if words[i] == '':
|
||||
if words[i] == "":
|
||||
continue
|
||||
else:
|
||||
lastfeedspeed[SPEEDS[i - 1]] = words[i]
|
||||
@@ -180,7 +218,7 @@ def parse(inputstring):
|
||||
else:
|
||||
s = "M3 S"
|
||||
s += str(abs(float(words[1])))
|
||||
output += s + '\n'
|
||||
output += s + "\n"
|
||||
|
||||
if words[0] in ["CG"]: # Gcode circle/arc
|
||||
if words[1] != "": # diameter mode
|
||||
@@ -193,8 +231,19 @@ def parse(inputstring):
|
||||
else: # CCW
|
||||
s = "G3"
|
||||
|
||||
s += " X" + words[2] + " Y" + words[3] + " I" + words[4] + " J" + words[5] + " F" + str(lastfeedspeed["XY"])
|
||||
output += s + '\n'
|
||||
s += (
|
||||
" X"
|
||||
+ words[2]
|
||||
+ " Y"
|
||||
+ words[3]
|
||||
+ " I"
|
||||
+ words[4]
|
||||
+ " J"
|
||||
+ words[5]
|
||||
+ " F"
|
||||
+ str(lastfeedspeed["XY"])
|
||||
)
|
||||
output += s + "\n"
|
||||
|
||||
last["X"] = words[2]
|
||||
last["Y"] = words[3]
|
||||
|
||||
@@ -1,24 +1,24 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#***************************************************************************
|
||||
#* Copyright (c) 2016 Christoph Blaue <blaue@fh-westkueste.de> *
|
||||
#* *
|
||||
#* 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. *
|
||||
#* *
|
||||
#* This program 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 Library General Public License for more details. *
|
||||
#* *
|
||||
#* You should have received a copy of the GNU Library General Public *
|
||||
#* License along with this program; if not, write to the Free Software *
|
||||
#* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 *
|
||||
#* USA *
|
||||
#* *
|
||||
#***************************************************************************
|
||||
# ***************************************************************************
|
||||
# * Copyright (c) 2016 Christoph Blaue <blaue@fh-westkueste.de> *
|
||||
# * *
|
||||
# * 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. *
|
||||
# * *
|
||||
# * This program 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 Library General Public License for more details. *
|
||||
# * *
|
||||
# * You should have received a copy of the GNU Library General Public *
|
||||
# * License along with this program; if not, write to the Free Software *
|
||||
# * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 *
|
||||
# * USA *
|
||||
# * *
|
||||
# ***************************************************************************
|
||||
|
||||
# 03-24-2021 Sliptonic: I've removed the PathUtils import and job lookup
|
||||
# post processors shouldn't be reaching back to the job. This can cause a
|
||||
@@ -30,7 +30,7 @@ import time
|
||||
from PathScripts import PostUtils
|
||||
import math
|
||||
|
||||
TOOLTIP = '''Post processor for Maho M 600E mill
|
||||
TOOLTIP = """Post processor for Maho M 600E mill
|
||||
|
||||
Machines with Philips or Heidenhain control should be very easy to adapt.
|
||||
|
||||
@@ -39,18 +39,18 @@ No programming experience required. This can make a generated g-code
|
||||
program more readable and since older machines have very
|
||||
limited memory it seems sensible to reduce the number of commands and
|
||||
parameters, like e.g. suppress the units in the header and at every hop.
|
||||
'''
|
||||
"""
|
||||
|
||||
#***************************************************************************
|
||||
# ***************************************************************************
|
||||
# user editable stuff here
|
||||
|
||||
COMMAND_SPACE = " "
|
||||
|
||||
MACHINE_NAME = 'Maho 600E'
|
||||
CORNER_MIN = {'x': -51.877, 'y': 0, 'z': 0} # use metric for internal units
|
||||
CORNER_MAX = {'x': 591.5, 'y': 391.498, 'z': 391.5} # use metric for internal units
|
||||
MACHINE_NAME = "Maho 600E"
|
||||
CORNER_MIN = {"x": -51.877, "y": 0, "z": 0} # use metric for internal units
|
||||
CORNER_MAX = {"x": 591.5, "y": 391.498, "z": 391.5} # use metric for internal units
|
||||
|
||||
UNITS = 'G21' # use metric units
|
||||
UNITS = "G21" # use metric units
|
||||
# possible values:
|
||||
# 'G20' for inches,
|
||||
# 'G21' for metric units.
|
||||
@@ -63,7 +63,7 @@ UNITS_INCLUDED = False # do not include the units in the GCode program
|
||||
# usually the units to be used are defined in the machine constants and almost never change,
|
||||
# so this can be set to False.
|
||||
|
||||
COMMENT = ''
|
||||
COMMENT = ""
|
||||
# possible values:
|
||||
# ';' centroid or sinumerik comment symbol,
|
||||
# '' leave blank for bracketed comments style "(comment comment comment)"
|
||||
@@ -108,7 +108,7 @@ MODAL = True
|
||||
# G1 X15 Y30
|
||||
|
||||
# suppress these parameters if they haven't changed
|
||||
MODALPARAMS = ['X', 'Y', 'Z', 'S', 'F']
|
||||
MODALPARAMS = ["X", "Y", "Z", "S", "F"]
|
||||
# possible values:
|
||||
# any list of GCode parameters
|
||||
# if a parameter doesn't change from one line to the next ( or even further) it is suppressed.
|
||||
@@ -126,7 +126,9 @@ SWAP_G2_G3 = True # some machines have the sign of the X-axis swapped, so they
|
||||
# this might be special with some maho machines or even with mine and
|
||||
# might be changed in the machine constants as well
|
||||
|
||||
SWAP_Y_Z = True # machines with an angle milling head do not switch axes, so we do it here
|
||||
SWAP_Y_Z = (
|
||||
True # machines with an angle milling head do not switch axes, so we do it here
|
||||
)
|
||||
# possible values:
|
||||
# True if Y and Z values have to be swapped
|
||||
# False do not swap
|
||||
@@ -161,8 +163,12 @@ RADIUS_COMMENT = True
|
||||
# and never with the comment symbol, because the radius might appear in
|
||||
# the middle of a line.
|
||||
|
||||
GCODE_MAP = {'M1': 'M0', 'M6': 'M66', 'G20': 'G70',
|
||||
'G21': 'G71'} # cb: this could be used to swap G2/G3
|
||||
GCODE_MAP = {
|
||||
"M1": "M0",
|
||||
"M6": "M66",
|
||||
"G20": "G70",
|
||||
"G21": "G71",
|
||||
} # cb: this could be used to swap G2/G3
|
||||
# possible values:
|
||||
# Comma separated list of values of the form 'sourceGCode':'targetGCode'
|
||||
#
|
||||
@@ -200,42 +206,56 @@ SUPPRESS_ZERO_FEED = True
|
||||
# def mkHeader(selection):
|
||||
# return ''
|
||||
|
||||
parser = argparse.ArgumentParser(prog='philips', add_help=False)
|
||||
parser.add_argument('--header', action='store_true', help='create header output')
|
||||
parser.add_argument('--no-header', action='store_true', help='suppress header output')
|
||||
parser = argparse.ArgumentParser(prog="philips", add_help=False)
|
||||
parser.add_argument("--header", action="store_true", help="create header output")
|
||||
parser.add_argument("--no-header", action="store_true", help="suppress header output")
|
||||
|
||||
parser.add_argument('--comments', action='store_true', help='create comment output')
|
||||
parser.add_argument('--no-comments', action='store_true', help='suppress comment output')
|
||||
parser.add_argument("--comments", action="store_true", help="create comment output")
|
||||
parser.add_argument(
|
||||
"--no-comments", action="store_true", help="suppress comment output"
|
||||
)
|
||||
|
||||
parser.add_argument('--line-numbers', action='store_true', help='prefix with line numbers')
|
||||
parser.add_argument('--no-line-numbers', action='store_true', help='omit line number prefixes')
|
||||
parser.add_argument(
|
||||
"--line-numbers", action="store_true", help="prefix with line numbers"
|
||||
)
|
||||
parser.add_argument(
|
||||
"--no-line-numbers", action="store_true", help="omit line number prefixes"
|
||||
)
|
||||
|
||||
parser.add_argument('--show-editor', action='store_true', help='pop up editor before writing output')
|
||||
parser.add_argument('--no-show-editor', action='store_true', help='don\'t pop up editor before writing output')
|
||||
parser.add_argument(
|
||||
"--show-editor", action="store_true", help="pop up editor before writing output"
|
||||
)
|
||||
parser.add_argument(
|
||||
"--no-show-editor",
|
||||
action="store_true",
|
||||
help="don't pop up editor before writing output",
|
||||
)
|
||||
|
||||
TOOLTIP_ARGS = parser.format_help()
|
||||
|
||||
|
||||
def processArguments(argstring):
|
||||
global LINENUMBERS
|
||||
global SHOW_EDITOR
|
||||
|
||||
for arg in argstring.split():
|
||||
if arg == '--line-numbers':
|
||||
if arg == "--line-numbers":
|
||||
LINENUMBERS = True
|
||||
elif arg == '--no-line-numbers':
|
||||
elif arg == "--no-line-numbers":
|
||||
LINENUMBERS = False
|
||||
elif arg == '--show-editor':
|
||||
elif arg == "--show-editor":
|
||||
SHOW_EDITOR = True
|
||||
elif arg == '--no-show-editor':
|
||||
elif arg == "--no-show-editor":
|
||||
SHOW_EDITOR = False
|
||||
|
||||
|
||||
def mkHeader(selection):
|
||||
# job = PathUtils.findParentJob(selection[0])
|
||||
# this is within a function, because otherwise filename and time don't change when changing the FreeCAD project
|
||||
# this is within a function, because otherwise filename and time don't change when changing the FreeCAD project
|
||||
# now = datetime.datetime.now()
|
||||
now = time.strftime("%Y-%m-%d %H:%M")
|
||||
originfile = FreeCAD.ActiveDocument.FileName
|
||||
headerNoNumber = "%PM\n" # this line gets no linenumber
|
||||
headerNoNumber = "%PM\n" # this line gets no linenumber
|
||||
# if hasattr(job, "Description"):
|
||||
# description = job.Description
|
||||
# else:
|
||||
@@ -244,18 +264,19 @@ def mkHeader(selection):
|
||||
# this line gets no linenumber, it is already a specially numbered
|
||||
headerNoNumber += "N9XXX (" + description + ", " + now + ")\n"
|
||||
header = ""
|
||||
# header += "(Output Time:" + str(now) + ")\n"
|
||||
# header += "(Output Time:" + str(now) + ")\n"
|
||||
header += "(" + originfile + ")\n"
|
||||
# header += "(Exported by FreeCAD)\n"
|
||||
# header += "(Exported by FreeCAD)\n"
|
||||
header += "(Post Processor: " + __name__ + ")\n"
|
||||
# header += "(Target machine: " + MACHINE_NAME + ")\n"
|
||||
header += "G18\n" # Select XY plane
|
||||
header += "G90\n" # Absolute coordinates
|
||||
header += "G51\n" # Reset Zero
|
||||
header += "G52 (ersetze G55-G59)" # set zero
|
||||
# header += "(Target machine: " + MACHINE_NAME + ")\n"
|
||||
header += "G18\n" # Select XY plane
|
||||
header += "G90\n" # Absolute coordinates
|
||||
header += "G51\n" # Reset Zero
|
||||
header += "G52 (ersetze G55-G59)" # set zero
|
||||
return headerNoNumber + linenumberify(header)
|
||||
|
||||
GCODE_HEADER = "" # do not terminate with a newline, it is inserted by linenumberify
|
||||
|
||||
GCODE_HEADER = "" # do not terminate with a newline, it is inserted by linenumberify
|
||||
# GCODE_HEADER = "G40 G90" # do not terminate with a newline, it is inserted by linenumberify
|
||||
# possible values:
|
||||
# any sequence of GCode, multiple lines are welcome
|
||||
@@ -270,11 +291,11 @@ GCODE_FOOTER = "M30"
|
||||
# linenumbers are inserted automatically if LINENUMBERS is True
|
||||
|
||||
# don't edit with the stuff below the next line unless you know what you're doing :)
|
||||
#***************************************************************************
|
||||
# ***************************************************************************
|
||||
|
||||
linenr = 0 # variable has to be global because it is used by linenumberify and export
|
||||
|
||||
if open.__module__ in ['__builtin__','io']:
|
||||
if open.__module__ in ["__builtin__", "io"]:
|
||||
pythonopen = open
|
||||
|
||||
|
||||
@@ -286,8 +307,21 @@ def angleUnder180(command, lastX, lastY, x, y, i, j):
|
||||
# (or on) the connection line
|
||||
middleOfLineY = (lastY + y) / 2
|
||||
centerY = lastY + j
|
||||
if ((command == 'G2' and ((lastX == x and ((lastY < y and i >= 0) or (lastY > y and i <= 0))) or (lastX < x and centerY <= middleOfLineY) or (lastX > x and centerY >= middleOfLineY)))
|
||||
or (command == 'G3' and ((lastX == x and ((lastY < y and i <= 0) or (lastY > y and i >= 0))) or (lastX < x and centerY >= middleOfLineY) or (lastX > x and centerY <= middleOfLineY)))):
|
||||
if (
|
||||
command == "G2"
|
||||
and (
|
||||
(lastX == x and ((lastY < y and i >= 0) or (lastY > y and i <= 0)))
|
||||
or (lastX < x and centerY <= middleOfLineY)
|
||||
or (lastX > x and centerY >= middleOfLineY)
|
||||
)
|
||||
) or (
|
||||
command == "G3"
|
||||
and (
|
||||
(lastX == x and ((lastY < y and i <= 0) or (lastY > y and i >= 0)))
|
||||
or (lastX < x and centerY >= middleOfLineY)
|
||||
or (lastX > x and centerY <= middleOfLineY)
|
||||
)
|
||||
):
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
@@ -299,10 +333,10 @@ def mapGCode(command):
|
||||
else:
|
||||
mappedCommand = command
|
||||
if SWAP_G2_G3:
|
||||
if command == 'G2':
|
||||
mappedCommand = 'G3'
|
||||
elif command == 'G3':
|
||||
mappedCommand = 'G2'
|
||||
if command == "G2":
|
||||
mappedCommand = "G3"
|
||||
elif command == "G3":
|
||||
mappedCommand = "G2"
|
||||
return mappedCommand
|
||||
|
||||
|
||||
@@ -312,7 +346,7 @@ def linenumberify(GCodeString):
|
||||
if not LINENUMBERS:
|
||||
result = GCodeString + "\n"
|
||||
else:
|
||||
result = ''
|
||||
result = ""
|
||||
strList = GCodeString.split("\n")
|
||||
for s in strList:
|
||||
if s:
|
||||
@@ -325,6 +359,7 @@ def linenumberify(GCodeString):
|
||||
result += s + "\n"
|
||||
return result
|
||||
|
||||
|
||||
def export(objectslist, filename, argstring):
|
||||
global UNITS
|
||||
global linenr
|
||||
@@ -333,14 +368,32 @@ def export(objectslist, filename, argstring):
|
||||
lastX = 0
|
||||
lastY = 0
|
||||
lastZ = 0
|
||||
params = ['X', 'Y', 'Z', 'A', 'B', 'I', 'J', 'F', 'H', 'S', 'T',
|
||||
'Q', 'R', 'L'] # Using XY plane most of the time so skipping K
|
||||
params = [
|
||||
"X",
|
||||
"Y",
|
||||
"Z",
|
||||
"A",
|
||||
"B",
|
||||
"I",
|
||||
"J",
|
||||
"F",
|
||||
"H",
|
||||
"S",
|
||||
"T",
|
||||
"Q",
|
||||
"R",
|
||||
"L",
|
||||
] # Using XY plane most of the time so skipping K
|
||||
modalParamsDict = dict()
|
||||
for mp in MODALPARAMS:
|
||||
modalParamsDict[mp] = None
|
||||
for obj in objectslist:
|
||||
if not hasattr(obj, "Path"):
|
||||
print("the object " + obj.Name + " is not a path. Please select only path and Compounds.")
|
||||
print(
|
||||
"the object "
|
||||
+ obj.Name
|
||||
+ " is not a path. Please select only path and Compounds."
|
||||
)
|
||||
return
|
||||
myMachine = None
|
||||
for pathobj in objectslist:
|
||||
@@ -354,23 +407,23 @@ def export(objectslist, filename, argstring):
|
||||
if myMachine is None:
|
||||
print("philips_post: No machine found in this selection")
|
||||
|
||||
gcode = ''
|
||||
gcode = ""
|
||||
gcode += mkHeader(objectslist)
|
||||
gcode += linenumberify(GCODE_HEADER)
|
||||
if UNITS_INCLUDED:
|
||||
gcode += linenumberify(mapGCode(UNITS))
|
||||
lastcommand = None
|
||||
for obj in objectslist:
|
||||
if hasattr(obj, 'Comment'):
|
||||
gcode += linenumberify('(' + obj.Comment + ')')
|
||||
if hasattr(obj, "Comment"):
|
||||
gcode += linenumberify("(" + obj.Comment + ")")
|
||||
for c in obj.Path.Commands:
|
||||
outstring = []
|
||||
command = c.Name
|
||||
if command != 'G0':
|
||||
command = command.replace('G0','G') # normalize: G01 -> G1
|
||||
if command != "G0":
|
||||
command = command.replace("G0", "G") # normalize: G01 -> G1
|
||||
|
||||
if (command != UNITS or UNITS_INCLUDED):
|
||||
if command[0] == '(':
|
||||
if command != UNITS or UNITS_INCLUDED:
|
||||
if command[0] == "(":
|
||||
command = PostUtils.fcoms(command, COMMENT)
|
||||
# the mapping is done for output only! For internal things we
|
||||
# still use the old value.
|
||||
@@ -378,63 +431,90 @@ def export(objectslist, filename, argstring):
|
||||
|
||||
if not MODAL or command != lastcommand:
|
||||
outstring.append(mappedCommand)
|
||||
# if MODAL == True:
|
||||
# #\better: append iff MODAL == False
|
||||
# if command == lastcommand:
|
||||
# outstring.pop(0)
|
||||
# if MODAL == True:
|
||||
# #\better: append iff MODAL == False
|
||||
# if command == lastcommand:
|
||||
# outstring.pop(0)
|
||||
if len(c.Parameters) >= 1:
|
||||
for param in params:
|
||||
# test print("param: " + param + ", command: " + command)
|
||||
if param in c.Parameters:
|
||||
if (param in MODALPARAMS) and (modalParamsDict[str(param)] == c.Parameters[str(param)]):
|
||||
if (param in MODALPARAMS) and (
|
||||
modalParamsDict[str(param)] == c.Parameters[str(param)]
|
||||
):
|
||||
# do nothing or append white space
|
||||
outstring.append(' ')
|
||||
elif param == 'F':
|
||||
feed = c.Parameters['F']
|
||||
outstring.append(" ")
|
||||
elif param == "F":
|
||||
feed = c.Parameters["F"]
|
||||
if SUPPRESS_ZERO_FEED and feed == 0:
|
||||
pass
|
||||
else:
|
||||
outstring.append(
|
||||
param + PostUtils.fmt(feed, FEED_DECIMALS, UNITS))
|
||||
elif param == 'H':
|
||||
outstring.append(
|
||||
param + str(int(c.Parameters['H'])))
|
||||
elif param == 'S':
|
||||
param
|
||||
+ PostUtils.fmt(feed, FEED_DECIMALS, UNITS)
|
||||
)
|
||||
elif param == "H":
|
||||
outstring.append(param + str(int(c.Parameters["H"])))
|
||||
elif param == "S":
|
||||
# rpm is unitless-therefore I had to 'fake it
|
||||
# out' by using metric units which don't get
|
||||
# converted from entered value
|
||||
outstring.append(
|
||||
param + PostUtils.fmt(c.Parameters['S'], SPINDLE_DECIMALS, 'G21'))
|
||||
elif param == 'T':
|
||||
outstring.append(
|
||||
param + str(int(c.Parameters['T'])))
|
||||
elif param == 'I' and (command == 'G2' or command == 'G3'):
|
||||
#test print("param = 'I'")
|
||||
param
|
||||
+ PostUtils.fmt(
|
||||
c.Parameters["S"], SPINDLE_DECIMALS, "G21"
|
||||
)
|
||||
)
|
||||
elif param == "T":
|
||||
outstring.append(param + str(int(c.Parameters["T"])))
|
||||
elif param == "I" and (command == "G2" or command == "G3"):
|
||||
# test print("param = 'I'")
|
||||
# this is the special case for circular paths,
|
||||
# where relative coordinates have to be changed
|
||||
# to absolute
|
||||
i = c.Parameters['I']
|
||||
i = c.Parameters["I"]
|
||||
# calculate the radius r
|
||||
j = c.Parameters['J']
|
||||
r = math.sqrt(i**2 + j**2)
|
||||
if USE_RADIUS_IF_POSSIBLE and angleUnder180(command, lastX, lastY, c.Parameters['X'], c.Parameters['Y'], i, j):
|
||||
j = c.Parameters["J"]
|
||||
r = math.sqrt(i ** 2 + j ** 2)
|
||||
if USE_RADIUS_IF_POSSIBLE and angleUnder180(
|
||||
command,
|
||||
lastX,
|
||||
lastY,
|
||||
c.Parameters["X"],
|
||||
c.Parameters["Y"],
|
||||
i,
|
||||
j,
|
||||
):
|
||||
outstring.append(
|
||||
'R' + PostUtils.fmt(r, AXIS_DECIMALS, UNITS))
|
||||
"R" + PostUtils.fmt(r, AXIS_DECIMALS, UNITS)
|
||||
)
|
||||
else:
|
||||
if RADIUS_COMMENT:
|
||||
outstring.append(
|
||||
'(R' + PostUtils.fmt(r, AXIS_DECIMALS, UNITS) + ')')
|
||||
"(R"
|
||||
+ PostUtils.fmt(r, AXIS_DECIMALS, UNITS)
|
||||
+ ")"
|
||||
)
|
||||
if ABSOLUTE_CIRCLE_CENTER:
|
||||
i += lastX
|
||||
outstring.append(
|
||||
param + PostUtils.fmt(i, AXIS_DECIMALS, UNITS))
|
||||
elif param == 'J' and (command == 'G2' or command == 'G3'):
|
||||
param + PostUtils.fmt(i, AXIS_DECIMALS, UNITS)
|
||||
)
|
||||
elif param == "J" and (command == "G2" or command == "G3"):
|
||||
# this is the special case for circular paths,
|
||||
# where incremental center has to be changed to
|
||||
# absolute center
|
||||
i = c.Parameters['I']
|
||||
j = c.Parameters['J']
|
||||
if USE_RADIUS_IF_POSSIBLE and angleUnder180(command, lastX, lastY, c.Parameters['X'], c.Parameters['Y'], i, j):
|
||||
i = c.Parameters["I"]
|
||||
j = c.Parameters["J"]
|
||||
if USE_RADIUS_IF_POSSIBLE and angleUnder180(
|
||||
command,
|
||||
lastX,
|
||||
lastY,
|
||||
c.Parameters["X"],
|
||||
c.Parameters["Y"],
|
||||
i,
|
||||
j,
|
||||
):
|
||||
# R is handled with the I parameter, here:
|
||||
# do nothing at all, keep the structure as
|
||||
# with I command
|
||||
@@ -445,19 +525,36 @@ def export(objectslist, filename, argstring):
|
||||
if SWAP_Y_Z:
|
||||
# we have to swap j and k as well
|
||||
outstring.append(
|
||||
'K' + PostUtils.fmt(j, AXIS_DECIMALS, UNITS))
|
||||
"K" + PostUtils.fmt(j, AXIS_DECIMALS, UNITS)
|
||||
)
|
||||
else:
|
||||
outstring.append(
|
||||
param + PostUtils.fmt(j, AXIS_DECIMALS, UNITS))
|
||||
elif param == 'K' and (command == 'G2' or command == 'G3'):
|
||||
param
|
||||
+ PostUtils.fmt(j, AXIS_DECIMALS, UNITS)
|
||||
)
|
||||
elif param == "K" and (command == "G2" or command == "G3"):
|
||||
# this is the special case for circular paths,
|
||||
# where incremental center has to be changed to
|
||||
# absolute center
|
||||
outstring.append(
|
||||
'(' + param + PostUtils.fmt(c.Parameters[param], AXIS_DECIMALS, UNITS) + ')')
|
||||
z = c.Parameters['Z']
|
||||
k = c.Parameters['K']
|
||||
if USE_RADIUS_IF_POSSIBLE and angleUnder180(command, lastX, lastY, c.Parameters['X'], c.Parameters['Y'], i, j):
|
||||
"("
|
||||
+ param
|
||||
+ PostUtils.fmt(
|
||||
c.Parameters[param], AXIS_DECIMALS, UNITS
|
||||
)
|
||||
+ ")"
|
||||
)
|
||||
z = c.Parameters["Z"]
|
||||
k = c.Parameters["K"]
|
||||
if USE_RADIUS_IF_POSSIBLE and angleUnder180(
|
||||
command,
|
||||
lastX,
|
||||
lastY,
|
||||
c.Parameters["X"],
|
||||
c.Parameters["Y"],
|
||||
i,
|
||||
j,
|
||||
):
|
||||
# R is handled with the I parameter, here:
|
||||
# do nothing at all, keep the structure as
|
||||
# with I command
|
||||
@@ -468,47 +565,60 @@ def export(objectslist, filename, argstring):
|
||||
if SWAP_Y_Z:
|
||||
# we have to swap j and k as well
|
||||
outstring.append(
|
||||
'J' + PostUtils.fmt(j, AXIS_DECIMALS, UNITS))
|
||||
"J" + PostUtils.fmt(j, AXIS_DECIMALS, UNITS)
|
||||
)
|
||||
else:
|
||||
outstring.append(
|
||||
param + PostUtils.fmt(j, AXIS_DECIMALS, UNITS))
|
||||
elif param == 'Y' and SWAP_Y_Z:
|
||||
param + PostUtils.fmt(j, AXIS_DECIMALS, UNITS)
|
||||
)
|
||||
elif param == "Y" and SWAP_Y_Z:
|
||||
outstring.append(
|
||||
'Z' + PostUtils.fmt(c.Parameters[param], AXIS_DECIMALS, UNITS))
|
||||
elif param == 'Z' and SWAP_Y_Z:
|
||||
"Z"
|
||||
+ PostUtils.fmt(
|
||||
c.Parameters[param], AXIS_DECIMALS, UNITS
|
||||
)
|
||||
)
|
||||
elif param == "Z" and SWAP_Y_Z:
|
||||
outstring.append(
|
||||
'Y' + PostUtils.fmt(c.Parameters[param], AXIS_DECIMALS, UNITS))
|
||||
"Y"
|
||||
+ PostUtils.fmt(
|
||||
c.Parameters[param], AXIS_DECIMALS, UNITS
|
||||
)
|
||||
)
|
||||
else:
|
||||
# To Do: suppress unknown commands, if this is done here, all X parameters are suppressed
|
||||
# To Do: suppress unknown commands, if this is done here, all X parameters are suppressed
|
||||
# this is an unknown command, don't create GCode for it
|
||||
# print("parameter " + param + " for command " + command + " ignored")
|
||||
# print("parameter " + param + " for command " + command + " ignored")
|
||||
outstring.append(
|
||||
param + PostUtils.fmt(c.Parameters[param], AXIS_DECIMALS, UNITS))
|
||||
param
|
||||
+ PostUtils.fmt(
|
||||
c.Parameters[param], AXIS_DECIMALS, UNITS
|
||||
)
|
||||
)
|
||||
|
||||
if param in MODALPARAMS:
|
||||
modalParamsDict[str(param)] = c.Parameters[
|
||||
param]
|
||||
modalParamsDict[str(param)] = c.Parameters[param]
|
||||
# save the last X, Y, Z values
|
||||
if 'X' in c.Parameters:
|
||||
lastX = c.Parameters['X']
|
||||
if 'Y' in c.Parameters:
|
||||
lastY = c.Parameters['Y']
|
||||
if 'Z' in c.Parameters:
|
||||
lastZ = c.Parameters['Z']
|
||||
if "X" in c.Parameters:
|
||||
lastX = c.Parameters["X"]
|
||||
if "Y" in c.Parameters:
|
||||
lastY = c.Parameters["Y"]
|
||||
if "Z" in c.Parameters:
|
||||
lastZ = c.Parameters["Z"]
|
||||
|
||||
outstr = ''
|
||||
outstr = ""
|
||||
for w in outstring:
|
||||
outstr += w + COMMAND_SPACE
|
||||
outstr = outstr.replace(']', '')
|
||||
outstr = outstr.replace('[', '')
|
||||
outstr = outstr.replace("'", '')
|
||||
outstr = outstr.replace(",", '.')
|
||||
outstr = outstr.replace("]", "")
|
||||
outstr = outstr.replace("[", "")
|
||||
outstr = outstr.replace("'", "")
|
||||
outstr = outstr.replace(",", ".")
|
||||
if LINENUMBERS:
|
||||
gcode += "N" + str(linenr) + " "
|
||||
linenr += LINENUMBER_INCREMENT
|
||||
gcode += outstr + '\n'
|
||||
gcode += outstr + "\n"
|
||||
lastcommand = c.Name
|
||||
gcode = gcode.replace("_","-")
|
||||
gcode = gcode.replace("_", "-")
|
||||
gcode += linenumberify(GCODE_FOOTER)
|
||||
if SHOW_EDITOR:
|
||||
PostUtils.editor(gcode)
|
||||
|
||||
@@ -1,28 +1,28 @@
|
||||
#***************************************************************************
|
||||
#* Copyright (c) 2015 Jon Nordby <jononor@gmail.com> *
|
||||
#* *
|
||||
#* 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) 2015 Jon Nordby <jononor@gmail.com> *
|
||||
# * *
|
||||
# * 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 *
|
||||
# * *
|
||||
# ***************************************************************************
|
||||
|
||||
|
||||
TOOLTIP='''
|
||||
TOOLTIP = """
|
||||
FreeCAD Path post-processor to output code for the Roland Modela MDX-## machines.
|
||||
|
||||
The machine speaks RML-1, specified in 'Roland RML-1 Programming Guidelines'
|
||||
@@ -32,14 +32,14 @@ http://altlab.org/d/content/m/pangelo/ideas/rml_command_guide_en_v100.pdf
|
||||
The format has some overlap with HPGL:
|
||||
https://en.wikipedia.org/wiki/HPGL
|
||||
http://paulbourke.net/dataformats/hpgl/
|
||||
'''
|
||||
"""
|
||||
|
||||
import FreeCAD
|
||||
import Part
|
||||
import PathScripts.PostUtils as PostUtils
|
||||
|
||||
# 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
|
||||
|
||||
|
||||
@@ -51,34 +51,46 @@ def export(objectslist, filename, argstring):
|
||||
for obj in objectslist:
|
||||
code += convertobject(obj)
|
||||
|
||||
gfile = pythonopen(filename,"w")
|
||||
gfile = pythonopen(filename, "w")
|
||||
gfile.write(code)
|
||||
gfile.close()
|
||||
|
||||
|
||||
def convertobject(obj):
|
||||
gcode = obj.Path.toGCode()
|
||||
gcode = parse(gcode)
|
||||
return gcode
|
||||
|
||||
|
||||
def motoron():
|
||||
return [ "!MC1;" ]
|
||||
return ["!MC1;"]
|
||||
|
||||
|
||||
def motoroff():
|
||||
return [ "!MC0;" ]
|
||||
return ["!MC0;"]
|
||||
|
||||
|
||||
def home():
|
||||
return [ "H;" ]
|
||||
return ["H;"]
|
||||
|
||||
|
||||
def setjog():
|
||||
# "!PZ%d,%d;",iz_down,iz_up); // set z down, jog
|
||||
return ""
|
||||
|
||||
|
||||
def addheader():
|
||||
return [ "PA;PA;" ] # absolute positioning
|
||||
return ["PA;PA;"] # absolute positioning
|
||||
|
||||
|
||||
def addfooter():
|
||||
return []
|
||||
|
||||
|
||||
def mm2cord(mm):
|
||||
mm = float(mm)
|
||||
return int(40.0*mm)
|
||||
return int(40.0 * mm)
|
||||
|
||||
|
||||
def feed(x=None, y=None, z=None, state=None):
|
||||
c = []
|
||||
@@ -87,13 +99,13 @@ def feed(x=None, y=None, z=None, state=None):
|
||||
|
||||
if x is not None:
|
||||
x = float(x)
|
||||
state['X'] = x
|
||||
state["X"] = x
|
||||
if y is not None:
|
||||
y = float(y)
|
||||
state['Y'] = y
|
||||
state["Y"] = y
|
||||
if z is not None:
|
||||
z = float(z)
|
||||
state['Z'] = z
|
||||
state["Z"] = z
|
||||
|
||||
if x is not None and y is not None and z is not None:
|
||||
# 3d motion
|
||||
@@ -105,6 +117,7 @@ def feed(x=None, y=None, z=None, state=None):
|
||||
pass
|
||||
return c
|
||||
|
||||
|
||||
def jog(x=None, y=None, z=None, state=None):
|
||||
c = []
|
||||
if state is None:
|
||||
@@ -112,22 +125,23 @@ def jog(x=None, y=None, z=None, state=None):
|
||||
if x is not None and y is not None:
|
||||
x, y = float(x), float(y)
|
||||
c.append("PU%d,%d;" % (mm2cord(x), mm2cord(y)))
|
||||
state['X'] = x
|
||||
state['Y'] = y
|
||||
state["X"] = x
|
||||
state["Y"] = y
|
||||
if z is not None:
|
||||
z = float(z)
|
||||
c.append("PU;")
|
||||
state['Z'] = z
|
||||
state["Z"] = z
|
||||
|
||||
return c
|
||||
|
||||
|
||||
def xyarc(args, state):
|
||||
# no native support in RML/Modela, convert to linear line segments
|
||||
c = []
|
||||
|
||||
lastPoint = FreeCAD.Vector(state['X'], state['Y'])
|
||||
newPoint = FreeCAD.Vector(float(args['X']), float(args['Y']))
|
||||
centerOffset = FreeCAD.Vector(float(args['I']), float(args['J']))
|
||||
lastPoint = FreeCAD.Vector(state["X"], state["Y"])
|
||||
newPoint = FreeCAD.Vector(float(args["X"]), float(args["Y"]))
|
||||
centerOffset = FreeCAD.Vector(float(args["I"]), float(args["J"]))
|
||||
center = lastPoint + centerOffset
|
||||
radius = (center - lastPoint).Length
|
||||
xyNormal = FreeCAD.Vector(0, 0, 1)
|
||||
@@ -135,14 +149,15 @@ def xyarc(args, state):
|
||||
p0 = circle.parameter(lastPoint)
|
||||
p1 = circle.parameter(newPoint)
|
||||
arc = Part.ArcOfCircle(circle, p0, p1)
|
||||
steps = 64 # specify max error instead?
|
||||
steps = 64 # specify max error instead?
|
||||
points = arc.discretize(steps)
|
||||
# consider direction?
|
||||
#print('p = Part.ArcOfCircle(Part.Circle(FreeCAD.Vector(%f, %f), FreeCAD.Vector(0, 0, 1), %f), %f, %f)' % (center.x, center.y, radius, p0, p1))
|
||||
# print('p = Part.ArcOfCircle(Part.Circle(FreeCAD.Vector(%f, %f), FreeCAD.Vector(0, 0, 1), %f), %f, %f)' % (center.x, center.y, radius, p0, p1))
|
||||
for p in points:
|
||||
c += feed(p.x, p.y, state['Z'], state)
|
||||
c += feed(p.x, p.y, state["Z"], state)
|
||||
return c
|
||||
|
||||
|
||||
def speed(xy=None, z=None, state=None):
|
||||
c = []
|
||||
if state is None:
|
||||
@@ -150,66 +165,67 @@ def speed(xy=None, z=None, state=None):
|
||||
print(xy, z, state)
|
||||
if xy is not None:
|
||||
xy = float(xy)
|
||||
if xy > 0.0 and xy != state['XYspeed']:
|
||||
if xy > 0.0 and xy != state["XYspeed"]:
|
||||
c.append("VS%.1f;" % xy)
|
||||
state['XYspeed'] = xy
|
||||
state["XYspeed"] = xy
|
||||
if z is not None:
|
||||
z = float(z)
|
||||
if z > 0.0 and z != state['Zspeed']:
|
||||
if z > 0.0 and z != state["Zspeed"]:
|
||||
c.append("!VZ%.1f;" % z)
|
||||
state['Zspeed'] = z
|
||||
state["Zspeed"] = z
|
||||
return c
|
||||
|
||||
|
||||
def convertgcode(cmd, args, state):
|
||||
"""Convert a single gcode command to equivalent Roland code"""
|
||||
if cmd == 'G0':
|
||||
if cmd == "G0":
|
||||
# jog
|
||||
return jog(args['X'], args['Y'], args['Z'], state)
|
||||
elif cmd == 'G1':
|
||||
return jog(args["X"], args["Y"], args["Z"], state)
|
||||
elif cmd == "G1":
|
||||
# linear feed
|
||||
c = []
|
||||
# feedrate
|
||||
c += speed(xy=args['F'], z=args['F'], state=state)
|
||||
c += speed(xy=args["F"], z=args["F"], state=state)
|
||||
# motion
|
||||
c += feed(args['X'], args['Y'], args['Z'], state)
|
||||
c += feed(args["X"], args["Y"], args["Z"], state)
|
||||
return c
|
||||
elif cmd == 'G2' or cmd == 'G3':
|
||||
elif cmd == "G2" or cmd == "G3":
|
||||
# arc feed
|
||||
c = []
|
||||
# feedrate
|
||||
c += speed(xy=args['F'], state=state)
|
||||
c += speed(xy=args["F"], state=state)
|
||||
# motion
|
||||
if args['X'] and args['Y'] and args['Z']:
|
||||
if args["X"] and args["Y"] and args["Z"]:
|
||||
# helical motion
|
||||
pass
|
||||
elif args['X'] and args['Y']:
|
||||
elif args["X"] and args["Y"]:
|
||||
# arc in plane
|
||||
c += xyarc(args, state)
|
||||
return c
|
||||
elif cmd == 'G20':
|
||||
elif cmd == "G20":
|
||||
# inches mode
|
||||
raise ValueError("rml_post: Inches mode not supported")
|
||||
elif cmd == 'G21':
|
||||
elif cmd == "G21":
|
||||
# millimeter mode
|
||||
return ""
|
||||
elif cmd == 'G40':
|
||||
elif cmd == "G40":
|
||||
# tool compensation off
|
||||
return ""
|
||||
elif cmd == 'G80':
|
||||
elif cmd == "G80":
|
||||
# cancel all cycles (drill normally)
|
||||
return "PU;"
|
||||
elif cmd == 'G81':
|
||||
elif cmd == "G81":
|
||||
c = []
|
||||
# feedrate
|
||||
c += speed(z=args['F'], state=state)
|
||||
c += speed(z=args["F"], state=state)
|
||||
# motion
|
||||
c += jog(args['X'], args['Y'], state=state)
|
||||
c += feed(args['X'], args['Y'], args['Z'], state)
|
||||
c += jog(args["X"], args["Y"], state=state)
|
||||
c += feed(args["X"], args["Y"], args["Z"], state)
|
||||
return c
|
||||
elif cmd == 'G90':
|
||||
elif cmd == "G90":
|
||||
# absolute mode?
|
||||
return ""
|
||||
elif cmd == 'G98':
|
||||
elif cmd == "G98":
|
||||
# feedrate
|
||||
return ""
|
||||
else:
|
||||
@@ -219,14 +235,14 @@ def convertgcode(cmd, args, state):
|
||||
def parse(inputstring):
|
||||
"parse(inputstring): returns a parsed output string"
|
||||
|
||||
state = { 'X': 0.0, 'Y': 0.0, 'Z': 0.0, 'XYspeed': -1.0, 'Zspeed': -1.0 }
|
||||
state = {"X": 0.0, "Y": 0.0, "Z": 0.0, "XYspeed": -1.0, "Zspeed": -1.0}
|
||||
output = []
|
||||
|
||||
# header
|
||||
output += addheader()
|
||||
output += motoron()
|
||||
|
||||
output += speed(2.0, 1.0, state) # defaults
|
||||
output += speed(2.0, 1.0, state) # defaults
|
||||
|
||||
# respect clearance height?
|
||||
|
||||
@@ -236,13 +252,13 @@ def parse(inputstring):
|
||||
if not line:
|
||||
continue
|
||||
parsed = PostUtils.stringsplit(line)
|
||||
command = parsed['command']
|
||||
print('cmd', line)
|
||||
command = parsed["command"]
|
||||
print("cmd", line)
|
||||
try:
|
||||
if command:
|
||||
code = convertgcode(command, parsed, state)
|
||||
if not isinstance(code, list):
|
||||
code = [ code ]
|
||||
code = [code]
|
||||
if len(code) and code[0]:
|
||||
output += code
|
||||
except NotImplementedError as e:
|
||||
@@ -253,7 +269,7 @@ def parse(inputstring):
|
||||
output += home()
|
||||
output += addfooter()
|
||||
|
||||
return '\n'.join(output)
|
||||
return "\n".join(output)
|
||||
|
||||
|
||||
# print (__name__ + " gcode postprocessor loaded.")
|
||||
|
||||
|
||||
@@ -34,7 +34,7 @@ from FreeCAD import Units
|
||||
import PathScripts.PathUtil as PathUtil
|
||||
import PathScripts.PostUtils as PostUtils
|
||||
|
||||
Revised = '2021-10-21' # Revision date for this file.
|
||||
Revised = "2021-10-21" # Revision date for this file.
|
||||
|
||||
# *****************************************************************************
|
||||
# * Due to the fundamentals of the FreeCAD pre-processor, *
|
||||
@@ -46,154 +46,145 @@ Revised = '2021-10-21' # Revision date for this file.
|
||||
# *****************************************************************************
|
||||
|
||||
|
||||
TOOLTIP = '''
|
||||
TOOLTIP = """
|
||||
Generate g-code from a Path that is compatible with the Duet controller (RepRapFirmware).
|
||||
import rrf_post
|
||||
rrf_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/min'
|
||||
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/min"
|
||||
|
||||
# *****************************************************************************
|
||||
# * 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
|
||||
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
|
||||
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_RRF_CONFIG = True # Display expected #defines for RRF 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_RRF_CONFIG = True # Display expected #defines for RRF 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
|
||||
OUTPUT_TOOL_CHANGE = True
|
||||
# *****************************************************************************
|
||||
# * Command line arguments *
|
||||
# *****************************************************************************
|
||||
parser = argparse.ArgumentParser(prog='rrf', add_help=False)
|
||||
parser = argparse.ArgumentParser(prog="rrf", 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("--rrf-config", action="store_true", help="output #defines for RRF")
|
||||
parser.add_argument(
|
||||
'--finish-comments',
|
||||
action='store_true',
|
||||
help='output finish-comment')
|
||||
"--no-rrf-config",
|
||||
action="store_true",
|
||||
help="suppress output #defines for RRF (default)",
|
||||
)
|
||||
parser.add_argument(
|
||||
'--no-finish-comments',
|
||||
action='store_true',
|
||||
help='suppress finish-comment output (default)')
|
||||
"--line-numbers", action="store_true", help="prefix with line numbers"
|
||||
)
|
||||
parser.add_argument(
|
||||
'--path-comments',
|
||||
action='store_true',
|
||||
help='output path-comment')
|
||||
"--no-line-numbers",
|
||||
action="store_true",
|
||||
help="do not prefix with line numbers (default)",
|
||||
)
|
||||
parser.add_argument(
|
||||
'--no-path-comments',
|
||||
action='store_true',
|
||||
help='suppress path-comment output (default)')
|
||||
"--show-editor",
|
||||
action="store_true",
|
||||
help="pop up editor before writing output (default)",
|
||||
)
|
||||
parser.add_argument(
|
||||
'--rrf-config',
|
||||
action='store_true',
|
||||
help='output #defines for RRF')
|
||||
"--no-show-editor",
|
||||
action="store_true",
|
||||
help="do not pop up editor before writing output",
|
||||
)
|
||||
parser.add_argument(
|
||||
'--no-rrf-config',
|
||||
action='store_true',
|
||||
help='suppress output #defines for RRF (default)')
|
||||
"--precision", default="3", help="number of digits of precision, default=3"
|
||||
)
|
||||
parser.add_argument(
|
||||
'--line-numbers',
|
||||
action='store_true',
|
||||
help='prefix with line numbers')
|
||||
"--translate_drill",
|
||||
action="store_true",
|
||||
help="translate drill cycles G81, G82, G83 into G0/G1 movements (default)",
|
||||
)
|
||||
parser.add_argument(
|
||||
'--no-line-numbers',
|
||||
action='store_true',
|
||||
help='do not prefix with line numbers (default)')
|
||||
"--no-translate_drill",
|
||||
action="store_true",
|
||||
help="do not translate drill cycles G81, G82, G83 into G0/G1 movements",
|
||||
)
|
||||
parser.add_argument(
|
||||
'--show-editor',
|
||||
action='store_true',
|
||||
help='pop up editor before writing output (default)')
|
||||
"--preamble", help='set commands to be issued before the first command, default=""'
|
||||
)
|
||||
parser.add_argument(
|
||||
'--no-show-editor',
|
||||
action='store_true',
|
||||
help='do not pop up editor before writing output')
|
||||
"--postamble", help='set commands to be issued after the last command, default="M5"'
|
||||
)
|
||||
parser.add_argument(
|
||||
'--precision',
|
||||
default='3',
|
||||
help='number of digits of precision, default=3')
|
||||
"--tool-change", action="store_true", help="Insert M6 for all tool changes"
|
||||
)
|
||||
parser.add_argument(
|
||||
'--translate_drill',
|
||||
action='store_true',
|
||||
help='translate drill cycles G81, G82, G83 into G0/G1 movements (default)')
|
||||
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()
|
||||
|
||||
# *****************************************************************************
|
||||
@@ -207,19 +198,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
|
||||
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 = ' '
|
||||
COMMAND_SPACE = " "
|
||||
# Global variables storing current position (Use None for safety.)
|
||||
CURRENT_X = None
|
||||
CURRENT_Y = None
|
||||
@@ -288,9 +279,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
|
||||
@@ -309,14 +300,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
|
||||
@@ -332,123 +323,129 @@ 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 RRF configuration to gcode file:
|
||||
if OUTPUT_RRF_CONFIG:
|
||||
gcode += linenumber() + '(RRF Configuration)\n'
|
||||
gcode += linenumber() + '(The following should be enabled in)\n'
|
||||
gcode += linenumber() + '(the config.g)\n'
|
||||
gcode += linenumber() + '(M453)\n'
|
||||
gcode += linenumber() + "(RRF Configuration)\n"
|
||||
gcode += linenumber() + "(The following should be enabled in)\n"
|
||||
gcode += linenumber() + "(the config.g)\n"
|
||||
gcode += linenumber() + "(M453)\n"
|
||||
|
||||
# Show the gcode result dialog:
|
||||
if FreeCAD.GuiUp and SHOW_EDITOR:
|
||||
@@ -462,26 +459,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()
|
||||
@@ -494,27 +491,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 = []
|
||||
@@ -531,107 +547,112 @@ 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 =='T':
|
||||
outlist.append(param + str(int(c.Parameters[param])))
|
||||
|
||||
elif param in ['H', 'D', 'S', 'P', 'L']:
|
||||
outlist.append(
|
||||
param
|
||||
+ format(
|
||||
float(feedRate.getValueAs(UNIT_FEED_FORMAT)),
|
||||
precision_string,
|
||||
)
|
||||
)
|
||||
elif param == "T":
|
||||
outlist.append(param + str(int(c.Parameters[param])))
|
||||
|
||||
elif param in ["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"
|
||||
# RRF: 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 + '\n'
|
||||
outlist[0] = ' '
|
||||
outlist[-1] =('T' + str(int(c.Parameters['T'])))
|
||||
out += linenumber() + line + "\n"
|
||||
outlist[0] = " "
|
||||
outlist[-1] = "T" + str(int(c.Parameters["T"]))
|
||||
|
||||
if not OUTPUT_TOOL_CHANGE and OUTPUT_COMMENTS:
|
||||
# next 2 lines could also be replaced by a single line as "outlist = []"
|
||||
outlist[0] = ' '
|
||||
outlist[-1] = ' '
|
||||
# next 2 lines could also be replaced by a single line as "outlist = []"
|
||||
outlist[0] = " "
|
||||
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)
|
||||
@@ -641,7 +662,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
|
||||
|
||||
@@ -662,61 +683,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)
|
||||
@@ -732,13 +748,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"
|
||||
# RRF 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:
|
||||
@@ -772,5 +788,3 @@ def drill_translate(outlist, cmd, params):
|
||||
# PEP8 format passed using: http://pep8online.com/, which primarily covers
|
||||
# indentation and line length. Some other aspects of PEP8 which have not
|
||||
# been applied yet may be applied in future updates.
|
||||
|
||||
|
||||
|
||||
@@ -21,16 +21,16 @@
|
||||
# * *
|
||||
# ***************************************************************************
|
||||
|
||||
'''
|
||||
"""
|
||||
This is an preprocessor to read gcode files produced from slic3r.
|
||||
'''
|
||||
"""
|
||||
|
||||
import os
|
||||
import Path
|
||||
import FreeCAD
|
||||
|
||||
# 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
|
||||
|
||||
|
||||
@@ -93,5 +93,4 @@ def parse(inputstring):
|
||||
return output
|
||||
|
||||
|
||||
print (__name__ + " gcode preprocessor loaded.")
|
||||
|
||||
print(__name__ + " gcode preprocessor loaded.")
|
||||
|
||||
@@ -30,7 +30,7 @@ import FreeCAD
|
||||
from FreeCAD import Units
|
||||
import shlex
|
||||
|
||||
TOOLTIP = '''
|
||||
TOOLTIP = """
|
||||
This is a postprocessor file for the Path workbench. It is used to
|
||||
take a pseudo-gcode fragment outputted by a Path object, and output
|
||||
real GCode suitable for a smoothieboard. This postprocessor, once placed
|
||||
@@ -39,25 +39,55 @@ FreeCAD, via the GUI importer or via python scripts with:
|
||||
|
||||
import smoothie_post
|
||||
smoothie_post.export(object,"/path/to/file.ncc","")
|
||||
'''
|
||||
"""
|
||||
|
||||
now = datetime.datetime.now()
|
||||
|
||||
parser = argparse.ArgumentParser(prog='linuxcnc', 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('--no-comments', action='store_true', help='suppress comment output')
|
||||
parser.add_argument('--line-numbers', action='store_true', help='prefix with line numbers')
|
||||
parser.add_argument('--no-line-numbers', action='store_true', help='don\'t prefix with line numbers (default)')
|
||||
parser.add_argument('--show-editor', action='store_true', help='pop up editor before writing output (default)')
|
||||
parser.add_argument('--no-show-editor', action='store_true', help='don\'t pop up editor before writing output')
|
||||
parser.add_argument('--precision', default='4', help='number of digits of precision, default=4')
|
||||
parser.add_argument('--preamble', help='set commands to be issued before the first command, default="G17\nG90"')
|
||||
parser.add_argument('--postamble', help='set commands to be issued after the last command, default="M05\nG17 G90\nM2"')
|
||||
parser.add_argument('--IP_ADDR', help='IP Address for machine target machine')
|
||||
parser.add_argument('--verbose', action='store_true', help='verbose output for debugging, default="False"')
|
||||
parser.add_argument('--inches', action='store_true', help='Convert output for US imperial mode (G20)')
|
||||
parser = argparse.ArgumentParser(prog="linuxcnc", 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(
|
||||
"--no-comments", action="store_true", help="suppress comment output"
|
||||
)
|
||||
parser.add_argument(
|
||||
"--line-numbers", action="store_true", help="prefix with line numbers"
|
||||
)
|
||||
parser.add_argument(
|
||||
"--no-line-numbers",
|
||||
action="store_true",
|
||||
help="don't prefix with line numbers (default)",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--show-editor",
|
||||
action="store_true",
|
||||
help="pop up editor before writing output (default)",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--no-show-editor",
|
||||
action="store_true",
|
||||
help="don't pop up editor before writing output",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--precision", default="4", help="number of digits of precision, default=4"
|
||||
)
|
||||
parser.add_argument(
|
||||
"--preamble",
|
||||
help='set commands to be issued before the first command, default="G17\nG90"',
|
||||
)
|
||||
parser.add_argument(
|
||||
"--postamble",
|
||||
help='set commands to be issued after the last command, default="M05\nG17 G90\nM2"',
|
||||
)
|
||||
parser.add_argument("--IP_ADDR", help="IP Address for machine target machine")
|
||||
parser.add_argument(
|
||||
"--verbose",
|
||||
action="store_true",
|
||||
help='verbose output for debugging, default="False"',
|
||||
)
|
||||
parser.add_argument(
|
||||
"--inches", action="store_true", help="Convert output for US imperial mode (G20)"
|
||||
)
|
||||
|
||||
TOOLTIP_ARGS = parser.format_help()
|
||||
|
||||
@@ -80,38 +110,38 @@ LINENR = 100 # line number starting value
|
||||
|
||||
# These globals will be reflected in the Machine configuration of the project
|
||||
UNITS = "G21" # G21 for metric, G20 for us standard
|
||||
UNIT_SPEED_FORMAT = 'mm/min'
|
||||
UNIT_FORMAT = 'mm'
|
||||
UNIT_SPEED_FORMAT = "mm/min"
|
||||
UNIT_FORMAT = "mm"
|
||||
|
||||
MACHINE_NAME = "SmoothieBoard"
|
||||
CORNER_MIN = {'x': 0, 'y': 0, 'z': 0}
|
||||
CORNER_MAX = {'x': 500, 'y': 300, 'z': 300}
|
||||
CORNER_MIN = {"x": 0, "y": 0, "z": 0}
|
||||
CORNER_MAX = {"x": 500, "y": 300, "z": 300}
|
||||
|
||||
# Preamble text will appear at the beginning of the GCODE output file.
|
||||
PREAMBLE = '''G17 G90
|
||||
'''
|
||||
PREAMBLE = """G17 G90
|
||||
"""
|
||||
|
||||
# Postamble text will appear following the last operation.
|
||||
POSTAMBLE = '''M05
|
||||
POSTAMBLE = """M05
|
||||
G17 G90
|
||||
M2
|
||||
'''
|
||||
"""
|
||||
|
||||
|
||||
# Pre operation text will be inserted before every operation
|
||||
PRE_OPERATION = ''''''
|
||||
PRE_OPERATION = """"""
|
||||
|
||||
# Post operation text will be inserted after every operation
|
||||
POST_OPERATION = ''''''
|
||||
POST_OPERATION = """"""
|
||||
|
||||
# Tool Change commands will be inserted before a tool change
|
||||
TOOL_CHANGE = ''''''
|
||||
TOOL_CHANGE = """"""
|
||||
|
||||
# Number of digits after the decimal point
|
||||
PRECISION = 5
|
||||
|
||||
# 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
|
||||
|
||||
|
||||
@@ -155,9 +185,9 @@ def processArguments(argstring):
|
||||
if args.postamble is not None:
|
||||
POSTAMBLE = args.postamble
|
||||
if args.inches:
|
||||
UNITS = 'G20'
|
||||
UNIT_SPEED_FORMAT = 'in/min'
|
||||
UNIT_FORMAT = 'in'
|
||||
UNITS = "G20"
|
||||
UNIT_SPEED_FORMAT = "in/min"
|
||||
UNIT_FORMAT = "in"
|
||||
|
||||
IP_ADDR = args.IP_ADDR
|
||||
VERBOSE = args.verbose
|
||||
@@ -173,7 +203,11 @@ def export(objectslist, filename, argstring):
|
||||
global UNITS
|
||||
for obj in objectslist:
|
||||
if not hasattr(obj, "Path"):
|
||||
FreeCAD.Console.PrintError("the object " + obj.Name + " is not a path. Please select only path and Compounds.\n")
|
||||
FreeCAD.Console.PrintError(
|
||||
"the object "
|
||||
+ obj.Name
|
||||
+ " is not a path. Please select only path and Compounds.\n"
|
||||
)
|
||||
return
|
||||
|
||||
FreeCAD.Console.PrintMessage("postprocessing...\n")
|
||||
@@ -245,7 +279,7 @@ def export(objectslist, filename, argstring):
|
||||
sendToSmoothie(IP_ADDR, final, filename)
|
||||
else:
|
||||
|
||||
if not filename == '-':
|
||||
if not filename == "-":
|
||||
gfile = pythonopen(filename, "w")
|
||||
gfile.write(final)
|
||||
gfile.close()
|
||||
@@ -260,7 +294,7 @@ def sendToSmoothie(ip, GCODE, fname):
|
||||
import os
|
||||
|
||||
fname = os.path.basename(fname)
|
||||
FreeCAD.Console.PrintMessage('sending to smoothie: {}\n'.format(fname))
|
||||
FreeCAD.Console.PrintMessage("sending to smoothie: {}\n".format(fname))
|
||||
|
||||
f = GCODE.rstrip()
|
||||
filesize = len(f)
|
||||
@@ -268,9 +302,9 @@ def sendToSmoothie(ip, GCODE, fname):
|
||||
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||
s.settimeout(4.0)
|
||||
s.connect((ip, 115))
|
||||
tn = s.makefile(mode='rw')
|
||||
tn = s.makefile(mode="rw")
|
||||
|
||||
# read startup prompt
|
||||
# read startup prompt
|
||||
ln = tn.readline()
|
||||
if not ln.startswith("+"):
|
||||
FreeCAD.Console.PrintMessage("Failed to connect with sftp: {}\n".format(ln))
|
||||
@@ -279,7 +313,7 @@ def sendToSmoothie(ip, GCODE, fname):
|
||||
if VERBOSE:
|
||||
print("RSP: " + ln.strip())
|
||||
|
||||
# Issue initial store command
|
||||
# Issue initial store command
|
||||
tn.write("STOR OLD /sd/" + fname + "\n")
|
||||
tn.flush()
|
||||
|
||||
@@ -291,7 +325,7 @@ def sendToSmoothie(ip, GCODE, fname):
|
||||
if VERBOSE:
|
||||
print("RSP: " + ln.strip())
|
||||
|
||||
# send size of file
|
||||
# send size of file
|
||||
tn.write("SIZE " + str(filesize) + "\n")
|
||||
tn.flush()
|
||||
|
||||
@@ -304,13 +338,13 @@ def sendToSmoothie(ip, GCODE, fname):
|
||||
print("RSP: " + ln.strip())
|
||||
|
||||
cnt = 0
|
||||
# now send file
|
||||
# now send file
|
||||
for line in f.splitlines(1):
|
||||
tn.write(line)
|
||||
if VERBOSE:
|
||||
cnt += len(line)
|
||||
print("SND: " + line.strip())
|
||||
print(str(cnt) + "/" + str(filesize) + "\r", end='')
|
||||
print(str(cnt) + "/" + str(filesize) + "\r", end="")
|
||||
|
||||
tn.flush()
|
||||
|
||||
@@ -322,7 +356,7 @@ def sendToSmoothie(ip, GCODE, fname):
|
||||
if VERBOSE:
|
||||
print("RSP: " + ln.strip())
|
||||
|
||||
# exit
|
||||
# exit
|
||||
tn.write("DONE\n")
|
||||
tn.flush()
|
||||
tn.close()
|
||||
@@ -343,12 +377,12 @@ def parse(pathobj):
|
||||
|
||||
out = ""
|
||||
lastcommand = None
|
||||
precision_string = '.' + str(PRECISION) + 'f'
|
||||
precision_string = "." + str(PRECISION) + "f"
|
||||
|
||||
# params = ['X','Y','Z','A','B','I','J','K','F','S'] #This list control
|
||||
# the order of parameters
|
||||
# linuxcnc doesn't want K properties on XY plane Arcs need work.
|
||||
params = ['X', 'Y', 'Z', 'A', 'B', 'I', 'J', 'F', 'S', 'T', 'Q', 'R', 'L']
|
||||
params = ["X", "Y", "Z", "A", "B", "I", "J", "F", "S", "T", "Q", "R", "L"]
|
||||
|
||||
if hasattr(pathobj, "Group"): # We have a compound or project.
|
||||
# if OUTPUT_COMMENTS:
|
||||
@@ -378,28 +412,42 @@ def parse(pathobj):
|
||||
# Now add the remaining parameters in order
|
||||
for param in params:
|
||||
if param in c.Parameters:
|
||||
if param == 'F':
|
||||
if c.Name not in ["G0", "G00"]: # linuxcnc doesn't use rapid speeds
|
||||
speed = Units.Quantity(c.Parameters['F'], FreeCAD.Units.Velocity)
|
||||
if param == "F":
|
||||
if c.Name not in [
|
||||
"G0",
|
||||
"G00",
|
||||
]: # linuxcnc doesn't use rapid speeds
|
||||
speed = Units.Quantity(
|
||||
c.Parameters["F"], FreeCAD.Units.Velocity
|
||||
)
|
||||
outstring.append(
|
||||
param + format(float(speed.getValueAs(UNIT_SPEED_FORMAT)), precision_string))
|
||||
elif param == 'T':
|
||||
outstring.append(param + str(c.Parameters['T']))
|
||||
elif param == 'S':
|
||||
outstring.append(param + str(c.Parameters['S']))
|
||||
SPINDLE_SPEED = c.Parameters['S']
|
||||
param
|
||||
+ format(
|
||||
float(speed.getValueAs(UNIT_SPEED_FORMAT)),
|
||||
precision_string,
|
||||
)
|
||||
)
|
||||
elif param == "T":
|
||||
outstring.append(param + str(c.Parameters["T"]))
|
||||
elif param == "S":
|
||||
outstring.append(param + str(c.Parameters["S"]))
|
||||
SPINDLE_SPEED = c.Parameters["S"]
|
||||
else:
|
||||
pos = Units.Quantity(c.Parameters[param], FreeCAD.Units.Length)
|
||||
outstring.append(
|
||||
param + format(float(pos.getValueAs(UNIT_FORMAT)), precision_string))
|
||||
if command in ['G1', 'G01', 'G2', 'G02', 'G3', 'G03']:
|
||||
outstring.append('S' + str(SPINDLE_SPEED))
|
||||
param
|
||||
+ format(
|
||||
float(pos.getValueAs(UNIT_FORMAT)), precision_string
|
||||
)
|
||||
)
|
||||
if command in ["G1", "G01", "G2", "G02", "G3", "G03"]:
|
||||
outstring.append("S" + str(SPINDLE_SPEED))
|
||||
|
||||
# store the latest command
|
||||
lastcommand = command
|
||||
|
||||
# Check for Tool Change:
|
||||
if command == 'M6':
|
||||
if command == "M6":
|
||||
# if OUTPUT_COMMENTS:
|
||||
# out += linenumber() + "(begin toolchange)\n"
|
||||
for line in TOOL_CHANGE.splitlines(True):
|
||||
|
||||
@@ -35,12 +35,13 @@ from FreeCAD import Units
|
||||
import Path
|
||||
import argparse
|
||||
import datetime
|
||||
|
||||
# import shlex
|
||||
from PathScripts import PostUtils
|
||||
|
||||
VERSION = "0.0.4"
|
||||
|
||||
TOOLTIP = ''' Post processor for UC-CNC.
|
||||
TOOLTIP = """ Post processor for UC-CNC.
|
||||
|
||||
This is a postprocessor file for the Path workbench. It is used to
|
||||
take a pseudo-gcode fragment outputted by a Path object, and output
|
||||
@@ -55,57 +56,57 @@ This postprocessor was tested on UC-CNC v1.2111, an UC100 and a Stepcraft 420.
|
||||
It was tested on FreeCAD v0.17, v0.18 and v0.19
|
||||
|
||||
Other (Stepcraft) machines using UC-CNC and UC* controllers should be easy to adapt.
|
||||
'''
|
||||
"""
|
||||
|
||||
# PREAMBLE_ possible values:
|
||||
# Multi line text with gcode. Preamble gcode
|
||||
# The preamble text will appear at the beginning of the GCODE output file.
|
||||
PREAMBLE_DEFAULT = '''G17 (Default: XY-plane)
|
||||
PREAMBLE_DEFAULT = """G17 (Default: XY-plane)
|
||||
G54 (Default: First coordinate system)
|
||||
G40 (Default: Cutter radius compensation none)
|
||||
G49 (Default: Tool Length Offsets: cancel tool length)
|
||||
G90 (Default: Absolute distance mode selection)
|
||||
G80 (Cancel canned cycle)
|
||||
'''
|
||||
"""
|
||||
|
||||
PREAMBLE_DEFAULT_NO_COMMENT = '''G17
|
||||
PREAMBLE_DEFAULT_NO_COMMENT = """G17
|
||||
G54
|
||||
G40
|
||||
G49
|
||||
G90
|
||||
G80
|
||||
'''
|
||||
"""
|
||||
|
||||
|
||||
# POSTAMBLE possible values:
|
||||
# Multi line text with gcode. Postable gcode
|
||||
# The postamble text will appear following the last operation.
|
||||
POSTAMBLE_DEFAULT = '''M05 (stop spindle)
|
||||
POSTAMBLE_DEFAULT = """M05 (stop spindle)
|
||||
G17 (Default: XY-plane)
|
||||
G54 (Default: First coordinate system)
|
||||
G40 (Default: Cutter radius compensation none)
|
||||
G90 (Default: Absolute distance mode selection)
|
||||
G80 (Cancel canned cycle)
|
||||
M30 (Stop program and rewind code)
|
||||
'''
|
||||
"""
|
||||
|
||||
POSTAMBLE_DEFAULT_NO_COMMENT = '''M05
|
||||
POSTAMBLE_DEFAULT_NO_COMMENT = """M05
|
||||
G17
|
||||
G54
|
||||
G40
|
||||
G90
|
||||
G80
|
||||
M30
|
||||
'''
|
||||
"""
|
||||
|
||||
# PRE_OPERATION: Pre operation text will be inserted before every operation
|
||||
PRE_OPERATION = ''''''
|
||||
PRE_OPERATION = """"""
|
||||
|
||||
# POST_OPERATION: Post operation text will be inserted after every operation
|
||||
POST_OPERATION = ''''''
|
||||
POST_OPERATION = """"""
|
||||
|
||||
# TOOL_CHANGE: Tool Change commands will be inserted before a tool change
|
||||
TOOL_CHANGE = ''''''
|
||||
TOOL_CHANGE = """"""
|
||||
|
||||
################################
|
||||
# Other configuration settings #
|
||||
@@ -209,8 +210,8 @@ PRECISION = 3
|
||||
# note: G20/G21 are not supported by UC-CNC, units are configured in a program profile.
|
||||
# In code G20/G21 commands are silently ignored by UC-CNC
|
||||
# UNITS is included in the post processor to mirror the profile settings.
|
||||
UNITS_US_IMP = 'G20'
|
||||
UNITS_METRIC = 'G21'
|
||||
UNITS_US_IMP = "G20"
|
||||
UNITS_METRIC = "G21"
|
||||
UNITS = UNITS_METRIC
|
||||
|
||||
# UNIT_FORMAT possible values: (see UNITS)
|
||||
@@ -220,8 +221,8 @@ UNITS = UNITS_METRIC
|
||||
# note: G20/G21 are not supported by UC-CNC, units are configured in a program profile.
|
||||
# In code G20/G21 commands are silently ignored by UC-CNC
|
||||
# UNITS is included in the post processor to mirror the profile settings.
|
||||
UNIT_FORMAT_US_IMP = 'in'
|
||||
UNIT_FORMAT_METRIC = 'mm'
|
||||
UNIT_FORMAT_US_IMP = "in"
|
||||
UNIT_FORMAT_METRIC = "mm"
|
||||
UNIT_FORMAT = UNIT_FORMAT_METRIC
|
||||
|
||||
# UNIT_SPEED_FORMAT possible values: (see UNITS)
|
||||
@@ -231,8 +232,8 @@ UNIT_FORMAT = UNIT_FORMAT_METRIC
|
||||
# note: G20/G21 are not supported by UC-CNC, units are configured in a program profile.
|
||||
# In code G20/G21 commands are silently ignored by UC-CNC
|
||||
# UNITS is included in the post processor to mirror the profile settings.
|
||||
UNIT_SPEED_FORMAT_US_IMP = 'in/min'
|
||||
UNIT_SPEED_FORMAT_METRIC = 'mm/min'
|
||||
UNIT_SPEED_FORMAT_US_IMP = "in/min"
|
||||
UNIT_SPEED_FORMAT_METRIC = "mm/min"
|
||||
UNIT_SPEED_FORMAT = UNIT_SPEED_FORMAT_METRIC
|
||||
|
||||
##################################################
|
||||
@@ -241,40 +242,49 @@ UNIT_SPEED_FORMAT = UNIT_SPEED_FORMAT_METRIC
|
||||
|
||||
# see: https://docs.python.org/3/library/argparse.html
|
||||
parser = argparse.ArgumentParser(prog=__name__, add_help=False)
|
||||
parser.add_argument('--name',
|
||||
help='GCode program name')
|
||||
parser.add_argument('--no-header', action='store_true',
|
||||
help='suppress header output')
|
||||
parser.add_argument('--no-comments', action='store_true',
|
||||
help='suppress comment output')
|
||||
parser.add_argument('--line-numbers', action='store_true',
|
||||
help='suppress prefix with line numbers')
|
||||
parser.add_argument('--no-show-editor', action='store_true',
|
||||
help='don\'t pop up editor before writing output')
|
||||
parser.add_argument('--precision', default='3',
|
||||
help='number of digits of precision, default=3')
|
||||
parser.add_argument('--preamble',
|
||||
help='set commands to be issued before the first command, default="G17\nG90\nG54"')
|
||||
parser.add_argument('--postamble',
|
||||
help='set commands to be issued after the last command, default="M05\nM30"')
|
||||
parser.add_argument('--inches', action='store_true',
|
||||
help='lengths in [in], G20')
|
||||
parser.add_argument('--metric', action='store_true',
|
||||
help='lengths in [mm], G21')
|
||||
parser.add_argument('--modal', action='store_true',
|
||||
help='repeat/suppress repeated command arguments')
|
||||
parser.add_argument('--tool-length-offset', action='store_true',
|
||||
help='suppress tool length offset G43 following tool changes')
|
||||
parser.add_argument('--repeat', action='store_true',
|
||||
help='repeat axis arguments')
|
||||
parser.add_argument("--name", help="GCode program name")
|
||||
parser.add_argument("--no-header", action="store_true", help="suppress header output")
|
||||
parser.add_argument(
|
||||
"--no-comments", action="store_true", help="suppress comment output"
|
||||
)
|
||||
parser.add_argument(
|
||||
"--line-numbers", action="store_true", help="suppress prefix with line numbers"
|
||||
)
|
||||
parser.add_argument(
|
||||
"--no-show-editor",
|
||||
action="store_true",
|
||||
help="don't pop up editor before writing output",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--precision", default="3", help="number of digits of precision, default=3"
|
||||
)
|
||||
parser.add_argument(
|
||||
"--preamble",
|
||||
help='set commands to be issued before the first command, default="G17\nG90\nG54"',
|
||||
)
|
||||
parser.add_argument(
|
||||
"--postamble",
|
||||
help='set commands to be issued after the last command, default="M05\nM30"',
|
||||
)
|
||||
parser.add_argument("--inches", action="store_true", help="lengths in [in], G20")
|
||||
parser.add_argument("--metric", action="store_true", help="lengths in [mm], G21")
|
||||
parser.add_argument(
|
||||
"--modal", action="store_true", help="repeat/suppress repeated command arguments"
|
||||
)
|
||||
parser.add_argument(
|
||||
"--tool-length-offset",
|
||||
action="store_true",
|
||||
help="suppress tool length offset G43 following tool changes",
|
||||
)
|
||||
parser.add_argument("--repeat", action="store_true", help="repeat axis arguments")
|
||||
TOOLTIP_ARGS = parser.format_help()
|
||||
|
||||
# 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
|
||||
|
||||
# to distinguish python built-in open function from the one declared below
|
||||
if open.__module__ == '__builtin__':
|
||||
if open.__module__ == "__builtin__":
|
||||
pythonopen = open
|
||||
|
||||
# debug option, trace to screen while processing to see where things break up.
|
||||
@@ -291,29 +301,29 @@ UNIT_DEFAULT_CHANGED = False
|
||||
warnings_count = 0
|
||||
problems_count = 0
|
||||
|
||||
HEADER = '''(Exported by FreeCAD for {})
|
||||
HEADER = """(Exported by FreeCAD for {})
|
||||
(Post Processor: {}, version {})
|
||||
(CAM file: {})
|
||||
(Output Time: {})
|
||||
'''
|
||||
"""
|
||||
|
||||
|
||||
def processArguments(argstring):
|
||||
global SHOW_EDITOR # Show gcode before saving.
|
||||
global PROG_NAME # Name of the G-Code program
|
||||
global OUTPUT_HEADER # Use of a document header
|
||||
global OUTPUT_COMMENTS # (Dont) use comments in output
|
||||
global OUTPUT_LINE_NUMBERS # (Dont) use line numbers in output
|
||||
global PREAMBLE # Preamble gcode
|
||||
global POSTAMBLE # Postable gcode
|
||||
global MODAL # Repeat/suppress repeated command arguments.
|
||||
global USE_TLO # Set tool length offset
|
||||
global PRECISION # Number of digits in feed and axis values
|
||||
global UNITS # Code to switch to specific units
|
||||
global UNIT_FORMAT # Text with specific units
|
||||
global UNIT_SPEED_FORMAT # Text with specific units over time units
|
||||
global UNIT_DEFAULT_CHANGED # tracing changes in UNIT settings.
|
||||
global REPEAT_ARGUMENTS # Repeat or suppress axis values if the same as previous line.
|
||||
global SHOW_EDITOR # Show gcode before saving.
|
||||
global PROG_NAME # Name of the G-Code program
|
||||
global OUTPUT_HEADER # Use of a document header
|
||||
global OUTPUT_COMMENTS # (Dont) use comments in output
|
||||
global OUTPUT_LINE_NUMBERS # (Dont) use line numbers in output
|
||||
global PREAMBLE # Preamble gcode
|
||||
global POSTAMBLE # Postable gcode
|
||||
global MODAL # Repeat/suppress repeated command arguments.
|
||||
global USE_TLO # Set tool length offset
|
||||
global PRECISION # Number of digits in feed and axis values
|
||||
global UNITS # Code to switch to specific units
|
||||
global UNIT_FORMAT # Text with specific units
|
||||
global UNIT_SPEED_FORMAT # Text with specific units over time units
|
||||
global UNIT_DEFAULT_CHANGED # tracing changes in UNIT settings.
|
||||
global REPEAT_ARGUMENTS # Repeat or suppress axis values if the same as previous line.
|
||||
|
||||
try:
|
||||
UNIT_DEFAULT_CHANGED = False
|
||||
@@ -381,14 +391,14 @@ def processArguments(argstring):
|
||||
|
||||
def append0(line):
|
||||
result = line
|
||||
if (trace_gcode):
|
||||
if trace_gcode:
|
||||
print("export: >>" + result)
|
||||
return result
|
||||
|
||||
|
||||
def append(line):
|
||||
result = linenumber() + line
|
||||
if (trace_gcode):
|
||||
if trace_gcode:
|
||||
print("export: >>" + result)
|
||||
return result
|
||||
|
||||
@@ -407,7 +417,11 @@ def export(objectslist, filename, argstring):
|
||||
|
||||
for obj in objectslist:
|
||||
if not hasattr(obj, "Path"):
|
||||
print("the object " + obj.Name + " is not a path. Please select only path and Compounds.")
|
||||
print(
|
||||
"the object "
|
||||
+ obj.Name
|
||||
+ " is not a path. Please select only path and Compounds."
|
||||
)
|
||||
return None
|
||||
|
||||
print("export: postprocessing...")
|
||||
@@ -419,16 +433,20 @@ def export(objectslist, filename, argstring):
|
||||
|
||||
# write header
|
||||
if OUTPUT_HEADER:
|
||||
for line in HEADER.format(GCODE_PROCESSOR,
|
||||
__name__, VERSION,
|
||||
FreeCAD.ActiveDocument.FileName, str(now)).splitlines(False):
|
||||
if (line):
|
||||
for line in HEADER.format(
|
||||
GCODE_PROCESSOR,
|
||||
__name__,
|
||||
VERSION,
|
||||
FreeCAD.ActiveDocument.FileName,
|
||||
str(now),
|
||||
).splitlines(False):
|
||||
if line:
|
||||
gcode += append(line + "\n")
|
||||
|
||||
# Write the preamble
|
||||
# G20/G21 not supported by UC-CNC, *always* report the configured units.
|
||||
gcode += append("(Units: '" + UNIT_FORMAT + "' and '" + UNIT_SPEED_FORMAT + "')\n")
|
||||
if (UNIT_DEFAULT_CHANGED):
|
||||
if UNIT_DEFAULT_CHANGED:
|
||||
gcode += append("(WARNING: Units default changed, check your UC-CNC profile)\n")
|
||||
warnings_count += 1
|
||||
|
||||
@@ -455,12 +473,12 @@ def export(objectslist, filename, argstring):
|
||||
# turn coolant on if required
|
||||
if hasattr(obj, "CoolantMode"):
|
||||
coolantMode = obj.CoolantMode
|
||||
if coolantMode == 'Mist':
|
||||
if coolantMode == "Mist":
|
||||
if OUTPUT_COMMENTS:
|
||||
gcode += append("M7 (coolant: mist on)\n")
|
||||
else:
|
||||
gcode += append("M7\n")
|
||||
if coolantMode == 'Flood':
|
||||
if coolantMode == "Flood":
|
||||
if OUTPUT_COMMENTS:
|
||||
gcode += append("M8 (coolant: flood on)\n")
|
||||
else:
|
||||
@@ -480,7 +498,7 @@ def export(objectslist, filename, argstring):
|
||||
# turn coolant off if required
|
||||
if hasattr(obj, "CoolantMode"):
|
||||
coolantMode = obj.CoolantMode
|
||||
if not coolantMode == 'None':
|
||||
if not coolantMode == "None":
|
||||
if OUTPUT_COMMENTS:
|
||||
gcode += append("M9 (coolant: off)\n")
|
||||
else:
|
||||
@@ -509,12 +527,15 @@ def export(objectslist, filename, argstring):
|
||||
final = gcode
|
||||
|
||||
if (0 < problems_count) or (0 < warnings_count):
|
||||
print("export: postprocessing: done, warnings: {}, problems: {}, see GCode for details."
|
||||
.format(warnings_count, problems_count))
|
||||
print(
|
||||
"export: postprocessing: done, warnings: {}, problems: {}, see GCode for details.".format(
|
||||
warnings_count, problems_count
|
||||
)
|
||||
)
|
||||
else:
|
||||
print("export: postprocessing: done (none of the problems detected).")
|
||||
|
||||
if not filename == '-':
|
||||
if not filename == "-":
|
||||
print("export: writing to '{}'".format(filename))
|
||||
gfile = pythonopen(filename, "w")
|
||||
gfile.write(final)
|
||||
@@ -526,7 +547,7 @@ def export(objectslist, filename, argstring):
|
||||
def linenumber():
|
||||
global LINENR
|
||||
|
||||
if (LINENR <= 0):
|
||||
if LINENR <= 0:
|
||||
LINENR = LINE_NUMBER_START
|
||||
if OUTPUT_LINE_NUMBERS is True:
|
||||
line = LINENR
|
||||
@@ -538,11 +559,28 @@ def linenumber():
|
||||
def parse(pathobj):
|
||||
out = ""
|
||||
lastcommand = None
|
||||
precision_string = '.' + str(PRECISION) + 'f'
|
||||
precision_string = "." + str(PRECISION) + "f"
|
||||
currLocation = {} # keep track for no doubles
|
||||
|
||||
# The params list control the order of parameters
|
||||
params = ['X', 'Y', 'Z', 'A', 'B', 'C', 'I', 'J', 'K', 'R', 'F', 'S', 'T', 'H', 'L', 'Q']
|
||||
params = [
|
||||
"X",
|
||||
"Y",
|
||||
"Z",
|
||||
"A",
|
||||
"B",
|
||||
"C",
|
||||
"I",
|
||||
"J",
|
||||
"K",
|
||||
"R",
|
||||
"F",
|
||||
"S",
|
||||
"T",
|
||||
"H",
|
||||
"L",
|
||||
"Q",
|
||||
]
|
||||
firstmove = Path.Command("G0", {"X": -1, "Y": -1, "Z": -1, "F": 0.0})
|
||||
currLocation.update(firstmove.Parameters) # set First location Parameters
|
||||
|
||||
@@ -574,49 +612,67 @@ def parse(pathobj):
|
||||
if command == lastcommand:
|
||||
commandlist.pop(0)
|
||||
|
||||
if c.Name[0] == '(' and not OUTPUT_COMMENTS: # command is a comment
|
||||
if c.Name[0] == "(" and not OUTPUT_COMMENTS: # command is a comment
|
||||
continue
|
||||
|
||||
# Now add the remaining parameters in order
|
||||
for param in params:
|
||||
if param in c.Parameters:
|
||||
if param == 'F' and (currLocation[param] != c.Parameters[param] or REPEAT_ARGUMENTS):
|
||||
if param == "F" and (
|
||||
currLocation[param] != c.Parameters[param] or REPEAT_ARGUMENTS
|
||||
):
|
||||
if c.Name not in ["G0", "G00"]: # No F in G0
|
||||
speed = Units.Quantity(c.Parameters['F'], FreeCAD.Units.Velocity)
|
||||
speed = Units.Quantity(
|
||||
c.Parameters["F"], FreeCAD.Units.Velocity
|
||||
)
|
||||
if speed.getValueAs(UNIT_SPEED_FORMAT) > 0.0:
|
||||
commandlist.append(
|
||||
param + format(float(speed.getValueAs(UNIT_SPEED_FORMAT)),
|
||||
precision_string))
|
||||
param
|
||||
+ format(
|
||||
float(speed.getValueAs(UNIT_SPEED_FORMAT)),
|
||||
precision_string,
|
||||
)
|
||||
)
|
||||
else:
|
||||
continue
|
||||
elif param == 'T':
|
||||
commandlist.append(param + str(int(c.Parameters['T'])))
|
||||
elif param == 'H':
|
||||
commandlist.append(param + str(int(c.Parameters['H'])))
|
||||
elif param == 'D':
|
||||
commandlist.append(param + str(int(c.Parameters['D'])))
|
||||
elif param == 'S':
|
||||
commandlist.append(param + str(int(c.Parameters['S'])))
|
||||
elif param == "T":
|
||||
commandlist.append(param + str(int(c.Parameters["T"])))
|
||||
elif param == "H":
|
||||
commandlist.append(param + str(int(c.Parameters["H"])))
|
||||
elif param == "D":
|
||||
commandlist.append(param + str(int(c.Parameters["D"])))
|
||||
elif param == "S":
|
||||
commandlist.append(param + str(int(c.Parameters["S"])))
|
||||
else:
|
||||
if (not REPEAT_ARGUMENTS) and (param in currLocation) and (currLocation[param] == c.Parameters[param]):
|
||||
if (
|
||||
(not REPEAT_ARGUMENTS)
|
||||
and (param in currLocation)
|
||||
and (currLocation[param] == c.Parameters[param])
|
||||
):
|
||||
continue
|
||||
else:
|
||||
pos = Units.Quantity(c.Parameters[param], FreeCAD.Units.Length)
|
||||
commandlist.append(param + format(float(pos.getValueAs(UNIT_FORMAT)),
|
||||
precision_string))
|
||||
pos = Units.Quantity(
|
||||
c.Parameters[param], FreeCAD.Units.Length
|
||||
)
|
||||
commandlist.append(
|
||||
param
|
||||
+ format(
|
||||
float(pos.getValueAs(UNIT_FORMAT)), precision_string
|
||||
)
|
||||
)
|
||||
|
||||
# store the latest command
|
||||
lastcommand = command
|
||||
currLocation.update(c.Parameters)
|
||||
|
||||
# Check for Tool Change:
|
||||
if command == 'M6':
|
||||
if command == "M6":
|
||||
for line in TOOL_CHANGE.splitlines(True):
|
||||
out += linenumber() + line
|
||||
|
||||
# add height offset
|
||||
if USE_TLO:
|
||||
tool_height = '\nG43 H' + str(int(c.Parameters['T']))
|
||||
tool_height = "\nG43 H" + str(int(c.Parameters["T"]))
|
||||
commandlist.append(tool_height)
|
||||
|
||||
if command == "message":
|
||||
@@ -633,7 +689,7 @@ def parse(pathobj):
|
||||
# append the line to the final output
|
||||
for w in commandlist:
|
||||
out += w.strip() + COMMAND_SPACE
|
||||
if (trace_gcode):
|
||||
if trace_gcode:
|
||||
print("parse : >>{}".format(out))
|
||||
out = out.strip() + "\n"
|
||||
|
||||
|
||||
Reference in New Issue
Block a user