Download src/Mod/CAM/Path/Post/scripts/uccnc_post.py from AbdulElahGwaith/FreeCAD: direct link, hf CLI and curl.
- Browser
- Download file 23.4 kB
-
https://huggingface.co/AbdulElahGwaith/FreeCAD/resolve/main/src/Mod/CAM/Path/Post/scripts/uccnc_post.py
- Command line
-
hf download hf://AbdulElahGwaith/FreeCAD/src/Mod/CAM/Path/Post/scripts/uccnc_post.py
-
curl -L -o uccnc_post.py https://huggingface.co/AbdulElahGwaith/FreeCAD/resolve/main/src/Mod/CAM/Path/Post/scripts/uccnc_post.py
23.4 kB
| # SPDX-License-Identifier: LGPL-2.1-or-later | |
| # *************************************************************************** | |
| # * * | |
| # * based upon linuxcnc_post.py (c) sliptonic (shopinthewoods@gmail.com) * | |
| # * * | |
| # * changed, but not enough to claim copyrights 2019-2021 * | |
| # * maintainer: A.H.M. Steenveld * | |
| # * * | |
| # * 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 * | |
| # * * | |
| # *************************************************************************** | |
| # See: https://wiki.freecad.org/Path_Post | |
| # https://wiki.freecad.org/Path_Postprocessor_Customization | |
| # for details on post processors like this one. | |
| import FreeCAD | |
| from FreeCAD import Units | |
| import Path | |
| import Path.Base.Util as PathUtil | |
| import PathScripts.PathUtils as PathUtils | |
| import argparse | |
| import datetime | |
| # import shlex | |
| import Path.Post.Utils as PostUtils | |
| from builtins import open as pyopen | |
| VERSION = "0.0.4" | |
| TOOLTIP = """ Post processor for UC-CNC. | |
| This is a postprocessor file for the Path workbench. It is used to | |
| take a pseudo-G-code fragment outputted by a Path object, and output | |
| real G-code. This postprocessor, once placed in the appropriate | |
| Path/Tool folder, can be used directly from inside FreeCAD, | |
| via the GUI importer or via python scripts with: | |
| import UCCNC_post | |
| UCCNC_post.export(object,"/path/to/file.ncc","") | |
| 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) | |
| 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 | |
| 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) | |
| 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 | |
| G17 | |
| G54 | |
| G40 | |
| G90 | |
| G80 | |
| M30 | |
| """ | |
| # PRE_OPERATION: Pre operation text will be inserted before every operation | |
| PRE_OPERATION = """""" | |
| # POST_OPERATION: Post operation text will be inserted after every operation | |
| POST_OPERATION = """""" | |
| # TOOL_CHANGE: Tool Change commands will be inserted before a tool change | |
| TOOL_CHANGE = """""" | |
| ################################ | |
| # Other configuration settings # | |
| ################################ | |
| # GCODE_PROCESSOR possible options | |
| # string The target GCode processor name. | |
| GCODE_PROCESSOR = "UC-CNC" | |
| # SHOW_EDITOR possible values: | |
| # bool Show gcode before saving. | |
| # True before the file is written it is shown to the user for inspection | |
| # False the file is written directly | |
| # set with --no-show-editor | |
| if FreeCAD.GuiUp: | |
| SHOW_EDITOR = True | |
| else: | |
| SHOW_EDITOR = False | |
| # PROG_NAME possible values: | |
| # text Name of the G-Code program | |
| # set with --name | |
| PROG_NAME = "prog1" | |
| # OUTPUT_HEADER possible values: | |
| # bool Use of a document header | |
| # True Use a predefined header. | |
| # False Do not use a predefined header. | |
| # set with --no-header | |
| OUTPUT_HEADER = True | |
| # OUTPUT_COMMENTS possible values: | |
| # bool (Dont) use comments in output | |
| # True Use comments in output. | |
| # False Suppress comments in output. | |
| # set with --no-comment | |
| OUTPUT_COMMENTS = True | |
| # OUTPUT_LINE_NUMBERS possible values: | |
| # bool (Dont) use line numbers in output | |
| # True Add a line number to each output line. | |
| # False Do not add a line number. | |
| # set with --line-numbers | |
| # note: line numbers Nxxxx are not supported by UC-CNC and are silently ignored. | |
| OUTPUT_LINE_NUMBERS = False | |
| # LINE_NUMBER_START possible values: | |
| # int unsigned integer. Line number starting value | |
| # 0..N | |
| LINE_NUMBER_START = 0 | |
| # LINE_NUMBER_STEP possible values: | |
| # int unsigned integer. Line number increment value | |
| # 1..N | |
| LINE_NUMBER_STEP = 1 | |
| # PREAMBLE possible values: | |
| # Multi line text with gcode. Preamble gcode | |
| # The preamble text will appear at the beginning of the GCODE output file. | |
| # set with --preamble | |
| PREAMBLE = PREAMBLE_DEFAULT | |
| # POSTAMBLE possible values: | |
| # Multi line text with gcode. Postable gcode | |
| # The postamble text will appear following the last operation. | |
| # set with --postable | |
| POSTAMBLE = POSTAMBLE_DEFAULT | |
| # MODAL possible values: | |
| # bool Repeat/suppress repeated command arguments. | |
| # True commands are suppressed if the same as previous line. | |
| # False commands are repeated if the same as previous line. | |
| # set with --modal | |
| MODAL = False | |
| # REPEAT_ARGUMENTS possible values: | |
| # bool Duplicate/suppressed axis values from the previous line. | |
| # True All arguments are repreated in each command. | |
| # False Equal values for arguments from the previous command are not repeated. | |
| # set with --repeat | |
| REPEAT_ARGUMENTS = False | |
| # USE_TLO possible values: | |
| # bool Set tool length offset. | |
| # True G43 will be output following tool changes | |
| # False No G43 used. | |
| # set with --tool-length-offset | |
| USE_TLO = False | |
| # PRECISION possible values: | |
| # int Number of digits in axis positions | |
| # 0...N | |
| # set with --precision N | |
| PRECISION = 3 | |
| # UNITS possible values: | |
| # GCODE Code to switch to specific units | |
| # G20 US imperial [inch] | |
| # G21 Metric [mm] | |
| # set with --inches | |
| # 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 = UNITS_METRIC | |
| # UNIT_FORMAT possible values: (see UNITS) | |
| # text Text with specific units | |
| # "inch" US imperial [inch] | |
| # "mm" Metric [mm] | |
| # 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 = UNIT_FORMAT_METRIC | |
| # UNIT_SPEED_FORMAT possible values: (see UNITS) | |
| # text Text with specific units over time units | |
| # "inch/min" US imperial [inch] | |
| # "mm/min" Metric [mm] | |
| # 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 = UNIT_SPEED_FORMAT_METRIC | |
| ################################################## | |
| # No more configuration settings after this line # | |
| ################################################## | |
| # 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\\nG54\\G40\\nG49\\nG90\\nG80\\n"', | |
| ) | |
| parser.add_argument( | |
| "--postamble", | |
| help='set commands to be issued after the last command, default="M05\\nG17\\nG54\\nG0\\nG90\\nG80\\nM30\\n"', | |
| ) | |
| 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() | |
| # debug option, trace to screen while processing to see where things break up. | |
| trace_gcode = False | |
| now = datetime.datetime.now() | |
| LINENR = 0 | |
| COMMAND_SPACE = " " | |
| UNIT_DEFAULT_CHANGED = False | |
| # counting warnings and problems. | |
| # Each warning/problem will appear as a WARNING:/PROBLEM: comment in the GCode output. | |
| warnings_count = 0 | |
| problems_count = 0 | |
| 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. | |
| try: | |
| UNIT_DEFAULT_CHANGED = False | |
| args = parser.parse_args(argstring.split()) | |
| if args.name is not None: | |
| PROG_NAME = args.name | |
| if args.no_header: | |
| OUTPUT_HEADER = False | |
| if args.no_comments: | |
| OUTPUT_COMMENTS = False | |
| if args.line_numbers: | |
| OUTPUT_LINE_NUMBERS = True | |
| if args.no_show_editor: | |
| SHOW_EDITOR = False | |
| PRECISION = args.precision | |
| if args.preamble is not None: | |
| PREAMBLE = args.preamble.replace("\\n", "\n") | |
| elif OUTPUT_COMMENTS: | |
| PREAMBLE = PREAMBLE_DEFAULT | |
| else: | |
| PREAMBLE = PREAMBLE_DEFAULT_NO_COMMENT | |
| if args.postamble is not None: | |
| POSTAMBLE = args.postamble.replace("\\n", "\n") | |
| elif OUTPUT_COMMENTS: | |
| POSTAMBLE = POSTAMBLE_DEFAULT | |
| else: | |
| POSTAMBLE = POSTAMBLE_DEFAULT_NO_COMMENT | |
| if args.inches and (UNITS != UNITS_US_IMP): | |
| print("Units: US Imperial [inch], check your UC-CNC profile.") | |
| UNITS = UNITS_US_IMP | |
| UNIT_FORMAT = UNIT_FORMAT_US_IMP | |
| UNIT_SPEED_FORMAT = UNIT_SPEED_FORMAT_US_IMP | |
| UNIT_DEFAULT_CHANGED = True | |
| if args.metric and (UNITS != UNITS_METRIC): | |
| print("Units: Metric [mm], check your UC-CNC profile.") | |
| UNITS = UNITS_METRIC | |
| UNIT_FORMAT = UNIT_FORMAT_METRIC | |
| UNIT_SPEED_FORMAT = UNIT_SPEED_FORMAT_METRIC | |
| UNIT_DEFAULT_CHANGED = True | |
| if args.modal: | |
| MODAL = True | |
| if args.tool_length_offset: | |
| USE_TLO = True | |
| if args.repeat: | |
| REPEAT_ARGUMENTS = True | |
| except Exception: | |
| return False | |
| return True | |
| def append0(line): | |
| result = line | |
| if trace_gcode: | |
| print("export: >>" + result) | |
| return result | |
| def append(line): | |
| result = linenumber() + line | |
| if trace_gcode: | |
| print("export: >>" + result) | |
| return result | |
| def export(objectslist, filename, argstring): | |
| if not processArguments(argstring): | |
| print("export: process arguments failed, '{}'".format(argstring)) | |
| return None | |
| global warnings_count | |
| global problems_count | |
| warnings_count = 0 | |
| problems_count = 0 | |
| for obj in objectslist: | |
| if not hasattr(obj, "Path"): | |
| print( | |
| "the object " + obj.Name + " is not a path. Please select only path and Compounds." | |
| ) | |
| return None | |
| print("export: postprocessing...") | |
| gcode = append0("%" + PROG_NAME + "\n") | |
| if not argstring: | |
| gcode += append("(" + __name__ + " with default settings)\n") | |
| else: | |
| gcode += append("({} {})\n".format(__name__, argstring)) | |
| # write header | |
| if OUTPUT_HEADER: | |
| 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: | |
| gcode += append("(WARNING: Units default changed, check your UC-CNC profile)\n") | |
| warnings_count += 1 | |
| if OUTPUT_COMMENTS: | |
| gcode += append("(preamble: begin)\n") | |
| # for obj in objectslist: | |
| # if isinstance(obj.Proxy, Path.Tool.Controller.ToolController): | |
| # gcode += append("(T{}={})\n".format(obj.ToolNumber, item.Name)) | |
| # error: global name 'PathScripts' is not defined | |
| for line in PREAMBLE.splitlines(): | |
| gcode += append(line + "\n") | |
| if OUTPUT_COMMENTS: | |
| gcode += append("(preamble: done)\n") | |
| # write the code body | |
| for obj in objectslist: | |
| # Skip inactive operations | |
| if not PathUtil.activeForOp(obj): | |
| continue | |
| # pre_op | |
| if OUTPUT_COMMENTS: | |
| gcode += append("(operation initialise: %s)\n" % obj.Label) | |
| for line in PRE_OPERATION.splitlines(True): | |
| gcode += append(line) | |
| # turn coolant on if required | |
| coolantMode = PathUtil.coolantModeForOp(obj) | |
| if coolantMode == "Mist": | |
| if OUTPUT_COMMENTS: | |
| gcode += append("M7 (coolant: mist on)\n") | |
| else: | |
| gcode += append("M7\n") | |
| if coolantMode == "Flood": | |
| if OUTPUT_COMMENTS: | |
| gcode += append("M8 (coolant: flood on)\n") | |
| else: | |
| gcode += append("M8\n") | |
| # process the operation gcode | |
| if OUTPUT_COMMENTS: | |
| gcode += append("(operation start: %s)\n" % obj.Label) | |
| gcode += parse(obj) | |
| if OUTPUT_COMMENTS: | |
| gcode += append("(operation done: %s)\n" % obj.Label) | |
| # post_op | |
| for line in POST_OPERATION.splitlines(True): | |
| gcode += append(line) | |
| # turn coolant off if required | |
| if not coolantMode == "None": | |
| if OUTPUT_COMMENTS: | |
| gcode += append("M9 (coolant: off)\n") | |
| else: | |
| gcode += append("M9\n") | |
| if OUTPUT_COMMENTS: | |
| gcode += append("(operation finalised: %s)\n" % obj.Label) | |
| # do the post_amble | |
| if OUTPUT_COMMENTS: | |
| gcode += append("(postamble: begin)\n") | |
| for line in POSTAMBLE.splitlines(True): | |
| gcode += append(line) | |
| if OUTPUT_COMMENTS: | |
| gcode += append("(postamble: done)\n") | |
| # Show the results | |
| if SHOW_EDITOR: | |
| dia = PostUtils.GCodeEditorDialog() | |
| dia.editor.setPlainText(gcode) | |
| result = dia.exec_() | |
| if result: | |
| final = dia.editor.toPlainText() | |
| else: | |
| final = gcode | |
| else: | |
| 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 | |
| ) | |
| ) | |
| else: | |
| print("export: postprocessing: done (none of the problems detected).") | |
| if not filename == "-": | |
| print("export: writing to '{}'".format(filename)) | |
| gfile = pyopen(filename, "w") | |
| gfile.write(final) | |
| gfile.close() | |
| return final | |
| def linenumber(): | |
| global LINENR | |
| if LINENR <= 0: | |
| LINENR = LINE_NUMBER_START | |
| if OUTPUT_LINE_NUMBERS is True: | |
| line = LINENR | |
| LINENR += LINE_NUMBER_STEP | |
| return "N{:03d} ".format(line) | |
| return "" | |
| def parse(pathobj): | |
| out = "" | |
| lastcommand = None | |
| precision_string = "." + str(PRECISION) + "f" | |
| currLocation = {} # keep track for no doubles | |
| # Command which can not skip parameters | |
| mandatoryCmd = ("G81", "G82", "G83") | |
| # Int parameters which can not be skipped | |
| mandatoryParamInt = ("D", "H", "S", "T") | |
| # Float parameters which can not be skipped | |
| mandatoryParamFloat = ("I", "J", "K") | |
| # 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") | |
| firstmove = Path.Command("G0", {"X": -1, "Y": -1, "Z": -1, "F": 0.0}) | |
| currLocation.update(firstmove.Parameters) # set First location Parameters | |
| if hasattr(pathobj, "Group"): | |
| # We have a compound or project. | |
| # if OUTPUT_COMMENTS: | |
| # 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"): | |
| return out | |
| # if OUTPUT_COMMENTS: | |
| # out += linenumber() + "(" + pathobj.Label + ")\n" | |
| for c in PathUtils.getPathWithPlacement(pathobj).Commands: | |
| commandlist = [] # list of elements in the command, code and params. | |
| command = c.Name.strip() # command M or G code or comment string | |
| commandlist.append(command) | |
| # if modal: only print the command if it is not the same as the last one | |
| if MODAL is True: | |
| if command == lastcommand: | |
| commandlist.pop(0) | |
| if c.Name.startswith("(") 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 c.Name not in ["G0", "G00"]: # No F in G0 | |
| 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, | |
| ) | |
| ) | |
| else: | |
| continue | |
| elif param in mandatoryParamInt: | |
| commandlist.append(param + str(int(c.Parameters[param]))) | |
| elif param in mandatoryParamFloat: | |
| commandlist.append( | |
| param + format(float(c.Parameters[param]), precision_string) | |
| ) | |
| else: | |
| if ( | |
| (not REPEAT_ARGUMENTS and c.Name not in mandatoryCmd) | |
| 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) | |
| ) | |
| # store the latest command | |
| lastcommand = command | |
| currLocation.update(c.Parameters) | |
| # Check for Tool Change: | |
| 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"])) | |
| commandlist.append(tool_height) | |
| if command == "message": | |
| if OUTPUT_COMMENTS is False: | |
| out = [] | |
| else: | |
| commandlist.pop(0) # remove the command | |
| # prepend a line number and append a newline | |
| if len(commandlist) >= 1: | |
| if OUTPUT_LINE_NUMBERS: | |
| commandlist.insert(0, (linenumber())) | |
| # append the line to the final output | |
| for w in commandlist: | |
| out += w.strip() + COMMAND_SPACE | |
| if trace_gcode: | |
| print("parse : >>{}".format(out)) | |
| out = out.strip() + "\n" | |
| return out | |