Download src/Mod/CAM/Path/Post/UtilsExport.py from AbdulElahGwaith/FreeCAD: direct link, hf CLI and curl.
- Browser
- Download file 16.6 kB
-
https://huggingface.co/AbdulElahGwaith/FreeCAD/resolve/main/src/Mod/CAM/Path/Post/UtilsExport.py
- Command line
-
hf download hf://AbdulElahGwaith/FreeCAD/src/Mod/CAM/Path/Post/UtilsExport.py
-
curl -L -o UtilsExport.py https://huggingface.co/AbdulElahGwaith/FreeCAD/resolve/main/src/Mod/CAM/Path/Post/UtilsExport.py
16.6 kB
| # SPDX-License-Identifier: LGPL-2.1-or-later | |
| # *************************************************************************** | |
| # * Copyright (c) 2014 Yorik van Havre <yorik@uncreated.net> * | |
| # * Copyright (c) 2014 sliptonic <shopinthewoods@gmail.com> * | |
| # * Copyright (c) 2015 Dan Falck <ddfalck@gmail.com> * | |
| # * Copyright (c) 2018, 2019 Gauthier Briere * | |
| # * Copyright (c) 2019, 2020 Schildkroet * | |
| # * Copyright (c) 2022-2025 Larry Woestman <LarryWoestman2@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 * | |
| # * * | |
| # *************************************************************************** | |
| import datetime | |
| import os | |
| from typing import Any, Dict, List | |
| import FreeCAD | |
| import Path.Base.Util as PathUtil | |
| import Path.Post.Utils as PostUtils | |
| import Path.Post.UtilsParse as PostUtilsParse | |
| import Path.Tool.Controller as PathToolController | |
| # Define some types that are used throughout this file | |
| Gcode = List[str] | |
| Values = Dict[str, Any] | |
| def check_canned_cycles(values: Values) -> None: | |
| """Check canned cycles for drilling.""" | |
| if values["TRANSLATE_DRILL_CYCLES"]: | |
| if len(values["SUPPRESS_COMMANDS"]) == 0: | |
| values["SUPPRESS_COMMANDS"] = ["G99", "G98", "G80"] | |
| else: | |
| values["SUPPRESS_COMMANDS"] += ["G99", "G98", "G80"] | |
| def output_coolant_off(values: Values, gcode: Gcode, coolant_mode: str) -> None: | |
| """Output the commands to turn coolant off if necessary.""" | |
| comment: str | |
| if values["ENABLE_COOLANT"] and coolant_mode != "None": | |
| if values["OUTPUT_COMMENTS"]: | |
| comment = PostUtilsParse.create_comment(values, f"Coolant Off: {coolant_mode}") | |
| gcode.append(f"{PostUtilsParse.linenumber(values)}{comment}") | |
| gcode.append(f"{PostUtilsParse.linenumber(values)}M9") | |
| def output_coolant_on(values: Values, gcode: Gcode, coolant_mode: str) -> None: | |
| """Output the commands to turn coolant on if necessary.""" | |
| comment: str | |
| if values["ENABLE_COOLANT"]: | |
| if values["OUTPUT_COMMENTS"] and coolant_mode != "None": | |
| comment = PostUtilsParse.create_comment(values, f"Coolant On: {coolant_mode}") | |
| gcode.append(f"{PostUtilsParse.linenumber(values)}{comment}") | |
| if coolant_mode == "Flood": | |
| gcode.append(f"{PostUtilsParse.linenumber(values)}M8") | |
| elif coolant_mode == "Mist": | |
| gcode.append(f"{PostUtilsParse.linenumber(values)}M7") | |
| def output_end_bcnc(values: Values, gcode: Gcode) -> None: | |
| """Output the ending BCNC header.""" | |
| comment: str | |
| if values["OUTPUT_BCNC"]: | |
| comment = PostUtilsParse.create_comment(values, "Block-name: post_amble") | |
| gcode.append(f"{PostUtilsParse.linenumber(values)}{comment}") | |
| comment = PostUtilsParse.create_comment(values, "Block-expand: 0") | |
| gcode.append(f"{PostUtilsParse.linenumber(values)}{comment}") | |
| comment = PostUtilsParse.create_comment(values, "Block-enable: 1") | |
| gcode.append(f"{PostUtilsParse.linenumber(values)}{comment}") | |
| def output_header(values: Values, gcode: Gcode) -> None: | |
| """Output the header.""" | |
| cam_file: str | |
| comment: str | |
| if not values["OUTPUT_HEADER"]: | |
| return | |
| comment = PostUtilsParse.create_comment(values, "Exported by FreeCAD") | |
| gcode.append(f"{PostUtilsParse.linenumber(values)}{comment}") | |
| comment = PostUtilsParse.create_comment( | |
| values, f'Post Processor: {values["POSTPROCESSOR_FILE_NAME"]}' | |
| ) | |
| gcode.append(f"{PostUtilsParse.linenumber(values)}{comment}") | |
| if FreeCAD.ActiveDocument: | |
| cam_file = os.path.basename(FreeCAD.ActiveDocument.FileName) | |
| else: | |
| cam_file = "<None>" | |
| comment = PostUtilsParse.create_comment(values, f"Cam File: {cam_file}") | |
| gcode.append(f"{PostUtilsParse.linenumber(values)}{comment}") | |
| comment = PostUtilsParse.create_comment(values, f"Output Time: {str(datetime.datetime.now())}") | |
| gcode.append(f"{PostUtilsParse.linenumber(values)}{comment}") | |
| def output_motion_mode(values: Values, gcode: Gcode) -> None: | |
| """Verify if PREAMBLE or SAFETYBLOCK have changed MOTION_MODE.""" | |
| if "G90" in values["PREAMBLE"] or "G90" in values["SAFETYBLOCK"]: | |
| values["MOTION_MODE"] = "G90" | |
| elif "G91" in values["PREAMBLE"] or "G91" in values["SAFETYBLOCK"]: | |
| values["MOTION_MODE"] = "G91" | |
| else: | |
| gcode.append(f'{PostUtilsParse.linenumber(values)}{values["MOTION_MODE"]}') | |
| def output_postamble_header(values: Values, gcode: Gcode) -> None: | |
| """Output the postamble header.""" | |
| comment: str = "" | |
| if values["OUTPUT_COMMENTS"]: | |
| comment = PostUtilsParse.create_comment(values, "Begin postamble") | |
| gcode.append(f"{PostUtilsParse.linenumber(values)}{comment}") | |
| def output_postamble(values: Values, gcode: Gcode) -> None: | |
| """Output the postamble.""" | |
| line: str | |
| for line in values["POSTAMBLE"].splitlines(False): | |
| gcode.append(f"{PostUtilsParse.linenumber(values)}{line}") | |
| def output_postop(values: Values, gcode: Gcode, obj) -> None: | |
| """Output the post-operation information.""" | |
| comment: str | |
| line: str | |
| if values["OUTPUT_COMMENTS"]: | |
| if values["SHOW_OPERATION_LABELS"]: | |
| comment = PostUtilsParse.create_comment( | |
| values, f'{values["FINISH_LABEL"]} operation: {obj.Label}' | |
| ) | |
| else: | |
| comment = PostUtilsParse.create_comment(values, f'{values["FINISH_LABEL"]} operation') | |
| gcode.append(f"{PostUtilsParse.linenumber(values)}{comment}") | |
| for line in values["POST_OPERATION"].splitlines(False): | |
| gcode.append(f"{PostUtilsParse.linenumber(values)}{line}") | |
| def output_preamble(values: Values, gcode: Gcode) -> None: | |
| """Output the preamble.""" | |
| comment: str | |
| line: str | |
| if values["OUTPUT_COMMENTS"]: | |
| comment = PostUtilsParse.create_comment(values, "Begin preamble") | |
| gcode.append(f"{PostUtilsParse.linenumber(values)}{comment}") | |
| for line in values["PREAMBLE"].splitlines(False): | |
| gcode.append(f"{PostUtilsParse.linenumber(values)}{line}") | |
| def output_preop(values: Values, gcode: Gcode, obj) -> None: | |
| """Output the pre-operation information.""" | |
| comment: str | |
| line: str | |
| if values["OUTPUT_COMMENTS"]: | |
| if values["SHOW_OPERATION_LABELS"]: | |
| comment = PostUtilsParse.create_comment(values, f"Begin operation: {obj.Label}") | |
| else: | |
| comment = PostUtilsParse.create_comment(values, "Begin operation") | |
| gcode.append(f"{PostUtilsParse.linenumber(values)}{comment}") | |
| if values["SHOW_MACHINE_UNITS"]: | |
| comment = PostUtilsParse.create_comment( | |
| values, f'Machine units: {values["UNIT_SPEED_FORMAT"]}' | |
| ) | |
| gcode.append(f"{PostUtilsParse.linenumber(values)}{comment}") | |
| if values["OUTPUT_MACHINE_NAME"]: | |
| comment = PostUtilsParse.create_comment( | |
| values, | |
| f'Machine: {values["MACHINE_NAME"]}, {values["UNIT_SPEED_FORMAT"]}', | |
| ) | |
| gcode.append(f"{PostUtilsParse.linenumber(values)}{comment}") | |
| for line in values["PRE_OPERATION"].splitlines(False): | |
| gcode.append(f"{PostUtilsParse.linenumber(values)}{line}") | |
| def output_return_to(values: Values, gcode: Gcode) -> None: | |
| """Output the RETURN_TO command.""" | |
| cmd: str | |
| num_x: str | |
| num_y: str | |
| num_z: str | |
| if values["RETURN_TO"]: | |
| num_x = values["RETURN_TO"][0] | |
| num_y = values["RETURN_TO"][1] | |
| num_z = values["RETURN_TO"][2] | |
| cmd = PostUtilsParse.format_command_line( | |
| values, ["G0", f"X{num_x}", f"Y{num_y}", f"Z{num_z}"] | |
| ) | |
| gcode.append(f"{PostUtilsParse.linenumber(values)}{cmd}") | |
| def output_safetyblock(values: Values, gcode: Gcode) -> None: | |
| """Output the safety block.""" | |
| line: str | |
| for line in values["SAFETYBLOCK"].splitlines(False): | |
| gcode.append(f"{PostUtilsParse.linenumber(values)}{line}") | |
| def output_start_bcnc(values: Values, gcode: Gcode, obj) -> None: | |
| """Output the starting BCNC header.""" | |
| comment: str | |
| if values["OUTPUT_BCNC"]: | |
| comment = PostUtilsParse.create_comment(values, f"Block-name: {obj.Label}") | |
| gcode.append(f"{PostUtilsParse.linenumber(values)}{comment}") | |
| comment = PostUtilsParse.create_comment(values, "Block-expand: 0") | |
| gcode.append(f"{PostUtilsParse.linenumber(values)}{comment}") | |
| comment = PostUtilsParse.create_comment(values, "Block-enable: 1") | |
| gcode.append(f"{PostUtilsParse.linenumber(values)}{comment}") | |
| def output_tool_list(values: Values, gcode: Gcode, objectslist) -> None: | |
| """Output a list of the tools used in the objects.""" | |
| comment: str | |
| if values["OUTPUT_COMMENTS"] and values["LIST_TOOLS_IN_PREAMBLE"]: | |
| for item in objectslist: | |
| if hasattr(item, "Proxy") and isinstance(item.Proxy, PathToolController.ToolController): | |
| comment = PostUtilsParse.create_comment(values, f"T{item.ToolNumber}={item.Name}") | |
| gcode.append(f"{PostUtilsParse.linenumber(values)}{comment}") | |
| def output_tool_return(values: Values, gcode: Gcode) -> None: | |
| """Output the tool return block.""" | |
| line: str | |
| for line in values["TOOLRETURN"].splitlines(False): | |
| gcode.append(f"{PostUtilsParse.linenumber(values)}{line}") | |
| def output_units(values: Values, gcode: Gcode) -> None: | |
| """Verify if PREAMBLE or SAFETYBLOCK have changed UNITS.""" | |
| if "G21" in values["PREAMBLE"] or "G21" in values["SAFETYBLOCK"]: | |
| values["UNITS"] = "G21" | |
| values["UNIT_FORMAT"] = "mm" | |
| values["UNIT_SPEED_FORMAT"] = "mm/min" | |
| elif "G20" in values["PREAMBLE"] or "G20" in values["SAFETYBLOCK"]: | |
| values["UNITS"] = "G20" | |
| values["UNIT_FORMAT"] = "in" | |
| values["UNIT_SPEED_FORMAT"] = "in/min" | |
| else: | |
| gcode.append(f'{PostUtilsParse.linenumber(values)}{values["UNITS"]}') | |
| def export_common(values: Values, objectslist, filename: str) -> str: | |
| """Do the common parts of postprocessing the objects in objectslist to filename.""" | |
| coolant_mode: str | |
| dia: PostUtils.GCodeEditorDialog | |
| final: str | |
| final_for_editor: str | |
| gcode: Gcode = [] | |
| editor_result: int = 1 | |
| for obj in objectslist: | |
| if not hasattr(obj, "Path"): | |
| print(f"The object {obj.Name} is not a path.") | |
| print("Please select only path and Compounds.") | |
| return "" | |
| print(f'PostProcessor: {values["POSTPROCESSOR_FILE_NAME"]} postprocessing...') | |
| check_canned_cycles(values) | |
| output_header(values, gcode) | |
| output_safetyblock(values, gcode) | |
| output_tool_list(values, gcode, objectslist) | |
| output_preamble(values, gcode) | |
| output_motion_mode(values, gcode) | |
| output_units(values, gcode) | |
| for obj in objectslist: | |
| # Skip inactive operations | |
| if not PathUtil.activeForOp(obj): | |
| continue | |
| coolant_mode = PathUtil.coolantModeForOp(obj) | |
| output_start_bcnc(values, gcode, obj) | |
| output_preop(values, gcode, obj) | |
| output_coolant_on(values, gcode, coolant_mode) | |
| # output the G-code for the group (compound) or simple path | |
| PostUtilsParse.parse_a_group(values, gcode, obj) | |
| output_postop(values, gcode, obj) | |
| output_coolant_off(values, gcode, coolant_mode) | |
| output_return_to(values, gcode) | |
| # | |
| # This doesn't make sense to me. It seems that both output_start_bcnc and | |
| # output_end_bcnc should be in the for loop or both should be out of the | |
| # for loop. However, that is the way that grbl post code was written, so | |
| # for now I will leave it that way until someone has time to figure it out. | |
| # | |
| output_end_bcnc(values, gcode) | |
| output_postamble_header(values, gcode) | |
| output_tool_return(values, gcode) | |
| output_safetyblock(values, gcode) | |
| output_postamble(values, gcode) | |
| # add the appropriate end-of-line characters to the gcode, including after the last line | |
| gcode.append("") | |
| if values["END_OF_LINE_CHARACTERS"] == "\n\n": | |
| # flag that we want to use "\n" as the end-of-line characters | |
| # by putting "\n\n" at the front of the gcode (which shouldn't otherwise happen) | |
| final = "\n\n" + "\n".join(gcode) | |
| else: | |
| # the other possibilities are: | |
| # "\n" means "use the end-of-line characters that match the system" | |
| # "\r" means "use \r" | |
| # "\r\n" means "use \r\n" | |
| final = values["END_OF_LINE_CHARACTERS"].join(gcode) | |
| if FreeCAD.GuiUp and values["SHOW_EDITOR"]: | |
| if len(final) > 100000: | |
| print("Skipping editor since output is greater than 100kb") | |
| else: | |
| # the editor expects lines to end in "\n", and returns lines ending in "\n" | |
| if values["END_OF_LINE_CHARACTERS"] == "\n": | |
| dia = PostUtils.GCodeEditorDialog(final, refactored=True) | |
| editor_result = dia.exec_() | |
| if editor_result == 1: | |
| final = dia.editor.toPlainText() | |
| else: | |
| final_for_editor = "\n".join(gcode) | |
| dia = PostUtils.GCodeEditorDialog(final_for_editor, refactored=True) | |
| editor_result = dia.exec_() | |
| if editor_result == 1: | |
| final_for_editor = dia.editor.toPlainText() | |
| # convert all "\n" to the appropriate end-of-line characters | |
| if values["END_OF_LINE_CHARACTERS"] == "\n\n": | |
| # flag that we want to use "\n" as the end-of-line characters | |
| # by putting "\n\n" at the front of the gcode | |
| # (which shouldn't otherwise happen) | |
| final = "\n\n" + final_for_editor | |
| else: | |
| # the other possibilities are: | |
| # "\r" means "use \r" | |
| # "\r\n" means "use \r\n" | |
| final = final_for_editor.replace("\n", values["END_OF_LINE_CHARACTERS"]) | |
| if editor_result == 0: | |
| print("aborted postprocessing.") | |
| return None | |
| if not filename == "-": | |
| if final[0:2] == "\n\n": | |
| # write out the gcode using "\n" as the end-of-line characters | |
| with open(filename, "w", encoding="utf-8", newline="") as gfile: | |
| gfile.write(final[2:]) | |
| elif "\r" in final: | |
| with open(filename, "w", encoding="utf-8", newline="") as gfile: | |
| # write out the gcode with whatever end-of-line characters it already has, | |
| # presumably either "\r" or "\r\n" | |
| gfile.write(final) | |
| else: | |
| with open(filename, "w", encoding="utf-8", newline=None) as gfile: | |
| # The gcode has "\n" as the end-of-line characters, which means | |
| # "write out the gcode with whatever end-of-line characters the system | |
| # that is running the postprocessor uses". | |
| gfile.write(final) | |
| print("done postprocessing.") | |
| return final | |