CAM: Refactor ToolBit.from_dict() for clarity and to generate more relevant warnings
This commit is contained in:
@@ -21,6 +21,7 @@
|
||||
# ***************************************************************************
|
||||
|
||||
import FreeCAD
|
||||
import Path
|
||||
import Path.Base.Util as PathUtil
|
||||
from typing import Dict, List, Any, Optional
|
||||
import tempfile
|
||||
@@ -76,9 +77,9 @@ def get_object_properties(
|
||||
properties[name] = getattr(obj, name)
|
||||
else:
|
||||
# Log a warning if a parameter expected by the shape class is missing
|
||||
FreeCAD.Console.PrintWarning(
|
||||
Path.Log.debug(
|
||||
f"Parameter '{name}' not found on object '{obj.Label}' "
|
||||
f"({obj.Name}). Default value will be used by the shape class.\n"
|
||||
f"({obj.Name}). Default value will be used by the shape class."
|
||||
)
|
||||
properties[name] = None # Indicate missing value
|
||||
return properties
|
||||
@@ -99,13 +100,13 @@ def update_shape_object_properties(
|
||||
try:
|
||||
PathUtil.setProperty(obj, name, value)
|
||||
except Exception as e:
|
||||
FreeCAD.Console.PrintWarning(
|
||||
Path.Log.warning(
|
||||
f"Failed to set property '{name}' on object '{obj.Label}'"
|
||||
f" ({obj.Name}) with value '{value}': {e}\n"
|
||||
f" ({obj.Name}) with value '{value}': {e}"
|
||||
)
|
||||
else:
|
||||
FreeCAD.Console.PrintWarning(
|
||||
f"Property '{name}' not found on object '{obj.Label}'" f" ({obj.Name}). Skipping.\n"
|
||||
Path.Log.warning(
|
||||
f"Property '{name}' not found on object '{obj.Label}' ({obj.Name}). Skipping."
|
||||
)
|
||||
|
||||
|
||||
@@ -184,6 +185,4 @@ class ShapeDocFromBytes:
|
||||
try:
|
||||
os.remove(self._temp_file)
|
||||
except Exception as e:
|
||||
FreeCAD.Console.PrintWarning(
|
||||
f"Failed to remove temporary file {self._temp_file}: {e}\n"
|
||||
)
|
||||
Path.Log.warning(f"Failed to remove temporary file {self._temp_file}: {e}")
|
||||
|
||||
@@ -287,7 +287,7 @@ class ToolBitShape(Asset):
|
||||
try:
|
||||
shape_class = ToolBitShape.get_shape_class_from_bytes(data)
|
||||
except Exception as e:
|
||||
Path.Log.warning(f"{id}: Failed to determine shape class from bytes: {e}")
|
||||
Path.Log.debug(f"{id}: Failed to determine shape class from bytes: {e}")
|
||||
shape_types = [c.name for c in ToolBitShape.__subclasses__()]
|
||||
shape_class = ToolBitShape.guess_subclass_from_name(id)
|
||||
if shape_class:
|
||||
|
||||
@@ -89,6 +89,49 @@ class ToolBit(Asset, ABC):
|
||||
return subclass
|
||||
raise ValueError(f"No ToolBit subclass found for shape {type(shape).__name__}")
|
||||
|
||||
@classmethod
|
||||
def _get_shape_type(cls, shape_id: str, shape_type: str | None) -> Type[ToolBitShape]:
|
||||
"""
|
||||
Extracts the shape class from the attributes dictionary.
|
||||
"""
|
||||
# Best method: if the shape-type is specified, use that.
|
||||
if shape_type:
|
||||
return ToolBitShape.get_subclass_by_name(shape_type)
|
||||
|
||||
# If no shape type is specified, try to find the shape class from the ID.
|
||||
shape_class = ToolBitShape.get_subclass_by_name(shape_id)
|
||||
if shape_class:
|
||||
return shape_class
|
||||
|
||||
# If that also fails, try to load the shape to get the class.
|
||||
Path.Log.debug(
|
||||
f'Failed to infer shape type from "{shape_id}", trying to load'
|
||||
f' the shape "{shape_id}" to determine the class. This may'
|
||||
" negatively impact performance."
|
||||
)
|
||||
shape_asset_uri = ToolBitShape.resolve_name(shape_id)
|
||||
shape = cam_assets.get(shape_asset_uri, depth=0)
|
||||
if shape:
|
||||
return shape.__class__
|
||||
|
||||
# If all else fails, try to guess the shape class from the ID.
|
||||
shape_types = [c.name for c in ToolBitShape.__subclasses__()]
|
||||
shape_class = ToolBitShape.guess_subclass_from_name(shape_id)
|
||||
if shape_class:
|
||||
Path.Log.warning(
|
||||
f'Failed to infer shape type from "{shape_id}",'
|
||||
f' guessing "{shape_class.name}".'
|
||||
f" To fix, name the body in the shape file to one of: {shape_types}"
|
||||
)
|
||||
return shape_class
|
||||
|
||||
# Default to endmill if nothing else works
|
||||
Path.Log.warning(
|
||||
f'Failed to infer shape type from {shape_id}, using "endmill".'
|
||||
f" To fix, name the body in the shape file to one of: {shape_types}"
|
||||
)
|
||||
return ToolBitShapeEndmill
|
||||
|
||||
@classmethod
|
||||
def from_dict(cls, attrs: Mapping, shallow: bool = False) -> "ToolBit":
|
||||
"""
|
||||
@@ -101,51 +144,28 @@ class ToolBit(Asset, ABC):
|
||||
if not shape_id:
|
||||
raise ValueError("ToolBit dictionary is missing 'shape' key")
|
||||
|
||||
# Find the shape type.
|
||||
shape_types = [c.name for c in ToolBitShape.__subclasses__()]
|
||||
shape_type = attrs.get("shape-type")
|
||||
shape_class = None
|
||||
if shape_type is None:
|
||||
shape_class = ToolBitShape.get_subclass_by_name(shape_id)
|
||||
if not shape_class:
|
||||
shape_class = ToolBitShape.guess_subclass_from_name(shape_id)
|
||||
if shape_class:
|
||||
Path.Log.warning(
|
||||
f"failed to infer shape type from {shape_id},"
|
||||
f' guessing "{shape_class.name}". To fix, name'
|
||||
f" the body in the shape file to one of: {shape_types}"
|
||||
)
|
||||
else:
|
||||
Path.Log.warning(
|
||||
f"failed to infer shape type from {shape_id},"
|
||||
f' using "endmill". To fix, name'
|
||||
f" the body in the shape file to one of: {shape_types}"
|
||||
)
|
||||
shape_class = ToolBitShapeEndmill
|
||||
shape_type = shape_class.name
|
||||
shape_class = cls._get_shape_type(shape_id, attrs.get("shape-type"))
|
||||
|
||||
# Try to load the shape, if the asset exists.
|
||||
tool_bit_shape = None
|
||||
# Create a ToolBitShape instance.
|
||||
if not shallow: # Shallow means: skip loading of child assets
|
||||
shape_asset_uri = ToolBitShape.resolve_name(shape_id)
|
||||
try:
|
||||
tool_bit_shape = cast(ToolBitShape, cam_assets.get(shape_asset_uri))
|
||||
except FileNotFoundError:
|
||||
pass # Rely on the fallback below
|
||||
Path.Log.debug(f"ToolBit.from_dict: Shape asset {shape_asset_uri} not found.")
|
||||
# Rely on the fallback below
|
||||
else:
|
||||
return cls.from_shape(tool_bit_shape, attrs, id=attrs.get("id"))
|
||||
|
||||
# If it does not exist, create a new instance from scratch.
|
||||
# Ending up here means we either could not load the shape asset,
|
||||
# or we are in shallow mode and do not want to load it.
|
||||
# Create a shape instance from scratch as a "placeholder".
|
||||
params = attrs.get("parameter", {})
|
||||
if tool_bit_shape is None:
|
||||
if not shape_class:
|
||||
shape_class = ToolBitShape.get_subclass_by_name(shape_type)
|
||||
if not shape_class:
|
||||
raise ValueError(f"failed to get shape class from {shape_id}")
|
||||
tool_bit_shape = shape_class(shape_id, **params)
|
||||
Path.Log.debug(
|
||||
f"ToolBit.from_dict: created shape instance {tool_bit_shape.name}"
|
||||
f" from {shape_id}. Uri: {tool_bit_shape.get_uri()}"
|
||||
)
|
||||
tool_bit_shape = shape_class(shape_id, **params)
|
||||
Path.Log.debug(
|
||||
f"ToolBit.from_dict: created shape instance {tool_bit_shape.name}"
|
||||
f" from {shape_id}. Uri: {tool_bit_shape.get_uri()}"
|
||||
)
|
||||
|
||||
# Now that we have a shape, create the toolbit instance.
|
||||
return cls.from_shape(tool_bit_shape, attrs, id=attrs.get("id"))
|
||||
|
||||
Reference in New Issue
Block a user