Download src/Mod/CAM/Path/Post/PostList.py from AbdulElahGwaith/FreeCAD: direct link, hf CLI and curl.
- Browser
- Download file 8.74 kB
-
https://huggingface.co/AbdulElahGwaith/FreeCAD/resolve/main/src/Mod/CAM/Path/Post/PostList.py
- Command line
-
hf download hf://AbdulElahGwaith/FreeCAD/src/Mod/CAM/Path/Post/PostList.py
-
curl -L -o PostList.py https://huggingface.co/AbdulElahGwaith/FreeCAD/resolve/main/src/Mod/CAM/Path/Post/PostList.py
8.74 kB
| # SPDX-License-Identifier: LGPL-2.1-or-later | |
| import re | |
| from typing import Any, List, Tuple | |
| import Path | |
| import Path.Base.Util as PathUtil | |
| import Path.Tool.Controller as PathToolController | |
| class _FixtureSetupObject: | |
| Path = None | |
| Name = "Fixture" | |
| InList = [] | |
| Label = "Fixture" | |
| class _CommandObject: | |
| def __init__(self, command): | |
| self.Path = Path.Path([command]) | |
| self.Name = "Command" | |
| self.InList = [] | |
| self.Label = "Command" | |
| def needsTcOp(oldTc: Any, newTc: Any) -> bool: | |
| return ( | |
| oldTc is None | |
| or oldTc.ToolNumber != newTc.ToolNumber | |
| or oldTc.SpindleSpeed != newTc.SpindleSpeed | |
| or oldTc.SpindleDir != newTc.SpindleDir | |
| ) | |
| def create_fixture_setup(processor: Any, order: int, fixture: str) -> _FixtureSetupObject: | |
| fobj = _FixtureSetupObject() | |
| c1 = Path.Command(fixture) | |
| fobj.Path = Path.Path([c1]) | |
| if order != 0: | |
| clearance_z = ( | |
| processor._job.Stock.Shape.BoundBox.ZMax | |
| + processor._job.SetupSheet.ClearanceHeightOffset.Value | |
| ) | |
| c2 = Path.Command(f"G0 Z{clearance_z}") | |
| fobj.Path.addCommands(c2) | |
| fobj.InList.append(processor._job) | |
| return fobj | |
| def build_postlist_by_fixture(processor: Any, early_tool_prep: bool = False) -> list: | |
| Path.Log.debug("Ordering by Fixture") | |
| postlist = [] | |
| wcslist = processor._job.Fixtures | |
| currTc = None | |
| for index, f in enumerate(wcslist): | |
| sublist = [create_fixture_setup(processor, index, f)] | |
| for obj in processor._operations: | |
| tc = PathUtil.toolControllerForOp(obj) | |
| if tc is not None and PathUtil.activeForOp(obj): | |
| if needsTcOp(currTc, tc): | |
| sublist.append(tc) | |
| Path.Log.debug(f"Appending TC: {tc.Name}") | |
| currTc = tc | |
| sublist.append(obj) | |
| postlist.append((f, sublist)) | |
| return postlist | |
| def build_postlist_by_tool(processor: Any, early_tool_prep: bool = False) -> list: | |
| Path.Log.debug("Ordering by Tool") | |
| postlist = [] | |
| wcslist = processor._job.Fixtures | |
| toolstring = "None" | |
| currTc = None | |
| fixturelist = [] | |
| for index, f in enumerate(wcslist): | |
| fixturelist.append(create_fixture_setup(processor, index, f)) | |
| curlist = [] | |
| sublist = [] | |
| def commitToPostlist(): | |
| if len(curlist) > 0: | |
| for fixture in fixturelist: | |
| sublist.append(fixture) | |
| sublist.extend(curlist) | |
| postlist.append((toolstring, sublist)) | |
| Path.Log.track(processor._job.PostProcessorOutputFile) | |
| for _, obj in enumerate(processor._operations): | |
| Path.Log.track(obj.Label) | |
| if not PathUtil.activeForOp(obj): | |
| Path.Log.track() | |
| continue | |
| tc = PathUtil.toolControllerForOp(obj) | |
| if tc is None or not needsTcOp(currTc, tc): | |
| curlist.append(obj) | |
| else: | |
| commitToPostlist() | |
| sublist = [tc] | |
| curlist = [obj] | |
| currTc = tc | |
| if "%T" in processor._job.PostProcessorOutputFile: | |
| toolstring = f"{tc.ToolNumber}" | |
| else: | |
| toolstring = re.sub(r"[^\w\d-]", "_", tc.Label) | |
| commitToPostlist() | |
| return postlist | |
| def build_postlist_by_operation(processor: Any, early_tool_prep: bool = False) -> list: | |
| Path.Log.debug("Ordering by Operation") | |
| postlist = [] | |
| wcslist = processor._job.Fixtures | |
| currTc = None | |
| for obj in processor._operations: | |
| if not PathUtil.activeForOp(obj): | |
| continue | |
| sublist = [] | |
| Path.Log.debug(f"obj: {obj.Name}") | |
| for index, f in enumerate(wcslist): | |
| sublist.append(create_fixture_setup(processor, index, f)) | |
| tc = PathUtil.toolControllerForOp(obj) | |
| if tc is not None: | |
| if processor._job.SplitOutput or needsTcOp(currTc, tc): | |
| sublist.append(tc) | |
| currTc = tc | |
| sublist.append(obj) | |
| postlist.append((obj.Label, sublist)) | |
| return postlist | |
| def buildPostList(processor: Any, early_tool_prep: bool = False) -> List[Tuple[str, List]]: | |
| orderby = processor._job.OrderOutputBy | |
| Path.Log.debug(f"Ordering by {orderby}") | |
| if orderby == "Fixture": | |
| postlist = build_postlist_by_fixture(processor, early_tool_prep) | |
| elif orderby == "Tool": | |
| postlist = build_postlist_by_tool(processor, early_tool_prep) | |
| elif orderby == "Operation": | |
| postlist = build_postlist_by_operation(processor, early_tool_prep) | |
| else: | |
| raise ValueError(f"Unknown order: {orderby}") | |
| Path.Log.debug(f"Postlist: {postlist}") | |
| if processor._job.SplitOutput: | |
| final_postlist = postlist | |
| else: | |
| final_postlist = [("allitems", [item for sublist in postlist for item in sublist[1]])] | |
| if early_tool_prep: | |
| return apply_early_tool_prep(final_postlist) | |
| return final_postlist | |
| def apply_early_tool_prep(postlist: List[Tuple[str, List]]) -> List[Tuple[str, List]]: | |
| """ | |
| Apply early tool preparation optimization to the postlist. | |
| This function modifies tool change commands to enable early tool preparation: | |
| - Always outputs tool changes as "Tn M6" (tool number followed by change command) | |
| - Additionally emits standalone "Tn" prep commands immediately after the previous M6 | |
| to allow the machine to prepare the next tool while the current tool is working | |
| Example output: | |
| T4 M6 <- change to tool 4 | |
| T5 <- prep tool 5 early (while T4 is working) | |
| <gcode> <- operations with T4 | |
| T5 M6 <- change to tool 5 (already prepped) | |
| T7 <- prep tool 7 early (while T5 is working) | |
| <gcode> <- operations with T5 | |
| T7 M6 <- change to tool 7 (already prepped) | |
| """ | |
| # First, collect all tool controllers across all groups to find next tool | |
| all_tool_controllers = [] | |
| for group_idx, (name, sublist) in enumerate(postlist): | |
| for item_idx, item in enumerate(sublist): | |
| if hasattr(item, "Proxy") and isinstance(item.Proxy, PathToolController.ToolController): | |
| all_tool_controllers.append((group_idx, item_idx, item)) | |
| new_postlist = [] | |
| for group_idx, (name, sublist) in enumerate(postlist): | |
| new_sublist = [] | |
| i = 0 | |
| while i < len(sublist): | |
| item = sublist[i] | |
| # Check if item is a tool controller | |
| if hasattr(item, "Proxy") and isinstance(item.Proxy, PathToolController.ToolController): | |
| # Tool controller has Path.Commands like: [Command (comment), Command M6 [T:n]] | |
| # Find the M6 command | |
| m6_cmd = None | |
| for cmd in item.Path.Commands: | |
| if cmd.Name == "M6": | |
| m6_cmd = cmd | |
| break | |
| if m6_cmd and len(m6_cmd.Parameters) > 0: | |
| # M6 command has parameters like {'T': 5}, access via key | |
| tool_number = m6_cmd.Parameters.get("T", item.ToolNumber) | |
| # Find this TC in the global list and check if there's a next one | |
| tc_position = next( | |
| ( | |
| idx | |
| for idx, (g_idx, i_idx, tc) in enumerate(all_tool_controllers) | |
| if g_idx == group_idx and i_idx == i | |
| ), | |
| None, | |
| ) | |
| next_tc = ( | |
| all_tool_controllers[tc_position + 1][2] | |
| if tc_position is not None and tc_position + 1 < len(all_tool_controllers) | |
| else None | |
| ) | |
| # Keep the original M6 command with tool parameter (M6 T5 format) | |
| # This is the valid FreeCAD format, postprocessor will reformat if needed | |
| new_sublist.append(item) | |
| # If there's a next tool controller, add Tn prep for it immediately after M6 | |
| if next_tc is not None: | |
| next_tool_number = next_tc.ToolNumber | |
| prep_next = Path.Command(f"T{next_tool_number}") | |
| prep_next_object = _CommandObject(prep_next) | |
| new_sublist.append(prep_next_object) | |
| else: | |
| # No M6 command found or no tool parameter, keep as-is | |
| new_sublist.append(item) | |
| else: | |
| new_sublist.append(item) | |
| i += 1 | |
| new_postlist.append((name, new_sublist)) | |
| return new_postlist | |