| |
|
|
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| import uuid |
| import json |
| from typing import Mapping, List, Optional, Type |
| import FreeCAD |
| from ...assets import Asset, AssetUri, AssetSerializer |
| from ...toolbit import ToolBit |
| from ...toolbit.mixins import RotaryToolBitMixin |
| from ...shape import ToolBitShape, ToolBitShapeEndmill |
| from ..models.library import Library |
|
|
| SHAPEMAP = { |
| "ballend": "Ballnose", |
| "endmill": "Cylindrical", |
| "v-bit": "Conical", |
| "vbit": "Conical", |
| "chamfer": "Snubnose", |
| } |
| SHAPEMAP_REVERSE = dict((v, k) for k, v in SHAPEMAP.items()) |
|
|
| tooltemplate = { |
| "units": "metric", |
| "shape": "Cylindrical", |
| "length": 10, |
| "diameter": 3.125, |
| "description": "", |
| } |
|
|
|
|
| class CamoticsLibrarySerializer(AssetSerializer): |
| for_class: Type[Asset] = Library |
| extensions: tuple[str] = (".json",) |
| mime_type: str = "application/json" |
|
|
| @classmethod |
| def get_label(cls) -> str: |
| return FreeCAD.Qt.translate("CAM", "Camotics Tool Library") |
|
|
| @classmethod |
| def extract_dependencies(cls, data: bytes) -> List[AssetUri]: |
| return [] |
|
|
| @classmethod |
| def serialize(cls, asset: Asset) -> bytes: |
| if not isinstance(asset, Library): |
| raise TypeError("Asset must be a Library instance") |
|
|
| toollist = {} |
| for tool_no, tool in asset._bit_nos.items(): |
| assert isinstance(tool, RotaryToolBitMixin) |
| toolitem = tooltemplate.copy() |
|
|
| diameter_value = tool.get_diameter() |
| |
| diameter_serializable = 2.0 |
| if isinstance(diameter_value, FreeCAD.Units.Quantity): |
| try: |
| val_mm = diameter_value.getValueAs("mm") |
| if val_mm is not None: |
| diameter_serializable = float(val_mm) |
| except ValueError: |
| |
| raw_val = diameter_value.Value if hasattr(diameter_value, "Value") else None |
| if isinstance(raw_val, (int, float)): |
| diameter_serializable = float(raw_val) |
| elif isinstance(diameter_value, (int, float)): |
| diameter_serializable = float(diameter_value) if diameter_value is not None else 2.0 |
|
|
| toolitem["diameter"] = diameter_serializable |
|
|
| toolitem["description"] = tool.label |
|
|
| length_value = tool.get_length() |
| |
| length_serializable = 10.0 |
| if isinstance(length_value, FreeCAD.Units.Quantity): |
| try: |
| val_mm = length_value.getValueAs("mm") |
| if val_mm is not None: |
| length_serializable = float(val_mm) |
| except ValueError: |
| |
| raw_val = length_value.Value if hasattr(length_value, "Value") else None |
| if isinstance(raw_val, (int, float)): |
| length_serializable = float(raw_val) |
| elif isinstance(length_value, (int, float)): |
| length_serializable = float(length_value) if length_value is not None else 10.0 |
|
|
| toolitem["length"] = length_serializable |
|
|
| toolitem["shape"] = SHAPEMAP.get(tool._tool_bit_shape.name.lower(), "Cylindrical") |
| toollist[str(tool_no)] = toolitem |
|
|
| return json.dumps(toollist, indent=2).encode("utf-8") |
|
|
| @classmethod |
| def deserialize( |
| cls, |
| data: bytes, |
| id: str, |
| dependencies: Optional[Mapping[AssetUri, Asset]], |
| ) -> Library: |
| try: |
| data_dict = json.loads(data.decode("utf-8")) |
| except json.JSONDecodeError as e: |
| raise ValueError(f"Failed to decode JSON data: {e}") from e |
|
|
| library = Library(id, id=id) |
| for tool_no_str, toolitem in data_dict.items(): |
| try: |
| tool_no = int(tool_no_str) |
| except ValueError: |
| print(f"Warning: Skipping invalid tool number: {tool_no_str}") |
| continue |
|
|
| |
| shape_name_str = SHAPEMAP_REVERSE.get(toolitem.get("shape", "Cylindrical"), "endmill") |
| shape_class = ToolBitShape.get_subclass_by_name(shape_name_str) |
| if not shape_class: |
| print(f"Warning: Unknown shape name '{shape_name_str}', defaulting to endmill") |
| shape_class = ToolBitShapeEndmill |
|
|
| |
| params = {} |
| try: |
| diameter = float(toolitem.get("diameter", 2)) |
| params["Diameter"] = FreeCAD.Units.Quantity(f"{diameter} mm") |
| except (ValueError, TypeError): |
| print(f"Warning: Invalid diameter for tool {tool_no_str}, skipping.") |
|
|
| try: |
| length = float(toolitem.get("length", 10)) |
| params["Length"] = FreeCAD.Units.Quantity(f"{length} mm") |
| except (ValueError, TypeError): |
| print(f"Warning: Invalid length for tool {tool_no_str}, skipping.") |
|
|
| |
| shape_id = shape_name_str.lower() |
| tool_bit_shape = shape_class(shape_id, **params) |
|
|
| |
| tool = ToolBit(tool_bit_shape, id=f"camotics_tool_{tool_no_str}") |
| tool.label = toolitem.get("description", "") |
|
|
| library.add_bit(tool, tool_no) |
|
|
| return library |
|
|
| @classmethod |
| def deep_deserialize(cls, data: bytes) -> Library: |
| |
| return cls.deserialize(data, str(uuid.uuid4()), {}) |
|
|