Files
pythonocc-step-editor/step_editor/scdm_capabilities.py
T

381 lines
16 KiB
Python

from __future__ import annotations
from collections.abc import Mapping
from dataclasses import dataclass
@dataclass(frozen=True)
class ScdmCapabilityDefinition:
key: str
display_name: str
object_types: tuple[str, ...]
value_kind: str
current_fields: tuple[str, ...]
default_intent: str
backend_operation: str
post_check: str
required_backend_command_groups: tuple[tuple[str, ...], ...] = ()
productized: bool = True
roadmap_stage: str = "S5"
block_reason: str = ""
def to_payload(self) -> dict[str, object]:
return {
"key": self.key,
"displayName": self.display_name,
"objectTypes": self.object_types,
"valueKind": self.value_kind,
"currentFields": self.current_fields,
"defaultIntent": self.default_intent,
"backendOperation": self.backend_operation,
"postCheck": self.post_check,
"requiredBackendCommandGroups": self.required_backend_command_groups,
"productized": self.productized,
"roadmapStage": self.roadmap_stage,
"blockReason": self.block_reason,
}
CAPABILITY_DEFINITIONS: dict[str, ScdmCapabilityDefinition] = {
"hole.diameter": ScdmCapabilityDefinition(
key="hole.diameter",
display_name="直径",
object_types=("hole", "cylindrical_hole", "cylindrical_face_group"),
value_kind="number",
current_fields=("geometry.diameter", "geometry.radius*2"),
default_intent="修改孔径",
backend_operation="change_hole_diameter",
post_check="target_hole_diameter",
required_backend_command_groups=(("StandardHoles",), ("OffsetFaces",)),
roadmap_stage="S5",
),
"hole.position": ScdmCapabilityDefinition(
key="hole.position",
display_name="位置",
object_types=("hole", "cylindrical_hole", "cylindrical_face_group"),
value_kind="vector3",
current_fields=("geometry.center", "geometry.axisCenter"),
default_intent="移动孔",
backend_operation="move_hole_axis",
post_check="target_hole_axis_center",
required_backend_command_groups=(("Move",),),
roadmap_stage="S5",
),
"face.offset": ScdmCapabilityDefinition(
key="face.offset",
display_name="偏移",
object_types=("face", "planar_face"),
value_kind="number",
current_fields=("geometry.offset", "geometry.planeOffset", "0"),
default_intent="推拉平面",
backend_operation="pull_face_offset",
post_check="target_face_offset",
required_backend_command_groups=(("OffsetFaces",),),
roadmap_stage="S5",
),
"feature.fill": ScdmCapabilityDefinition(
key="feature.fill",
display_name="填孔/删除小特征",
object_types=("hole", "small_feature"),
value_kind="command",
current_fields=("1",),
default_intent="删除并补面",
backend_operation="fill_feature",
post_check="target_feature_removed",
required_backend_command_groups=(("Fill",), ("Delete",)),
roadmap_stage="S5",
),
"slot.width": ScdmCapabilityDefinition(
key="slot.width",
display_name="槽宽",
object_types=("slot", "obround_slot", "rectangular_slot"),
value_kind="number",
current_fields=("geometry.width",),
default_intent="修改槽宽",
backend_operation="change_slot_width",
post_check="target_slot_width",
productized=False,
roadmap_stage="S7.2",
block_reason="槽宽属于 S7 第二批能力,真实 SCDM 命令和 STEP 样例回测尚未完成。",
),
"slot.depth": ScdmCapabilityDefinition(
key="slot.depth",
display_name="槽深",
object_types=("slot", "obround_slot", "rectangular_slot"),
value_kind="number",
current_fields=("geometry.depth",),
default_intent="修改槽深",
backend_operation="change_slot_depth",
post_check="target_slot_depth",
productized=False,
roadmap_stage="S7.2",
block_reason="槽深属于 S7 第二批能力,真实 SCDM 命令和 STEP 样例回测尚未完成。",
),
"slot.position": ScdmCapabilityDefinition(
key="slot.position",
display_name="槽位置",
object_types=("slot", "obround_slot", "rectangular_slot"),
value_kind="vector3",
current_fields=("geometry.center", "geometry.axisCenter"),
default_intent="移动槽",
backend_operation="move_slot",
post_check="target_slot_center",
required_backend_command_groups=(("Move",),),
roadmap_stage="S7.2",
),
"boss.height": ScdmCapabilityDefinition(
key="boss.height",
display_name="凸台高度",
object_types=("boss", "cylindrical_boss", "rectangular_boss"),
value_kind="number",
current_fields=("geometry.height",),
default_intent="修改凸台高度",
backend_operation="change_boss_height",
post_check="target_boss_height",
productized=False,
roadmap_stage="S7.3",
block_reason="凸台高度属于 S7 第三批能力,真实 SCDM 命令和 STEP 样例回测尚未完成。",
),
"boss.diameter": ScdmCapabilityDefinition(
key="boss.diameter",
display_name="凸台直径",
object_types=("boss", "cylindrical_boss"),
value_kind="number",
current_fields=("geometry.diameter", "geometry.radius*2"),
default_intent="修改凸台直径",
backend_operation="change_boss_diameter",
post_check="target_boss_diameter",
productized=False,
roadmap_stage="S7.3",
block_reason="凸台直径属于 S7 第三批能力,真实 SCDM 命令和 STEP 样例回测尚未完成。",
),
"boss.position": ScdmCapabilityDefinition(
key="boss.position",
display_name="凸台位置",
object_types=("boss", "cylindrical_boss", "rectangular_boss"),
value_kind="vector3",
current_fields=("geometry.center", "geometry.axisCenter"),
default_intent="移动凸台",
backend_operation="move_boss",
post_check="target_boss_center",
required_backend_command_groups=(("Move",),),
roadmap_stage="S7.3",
),
"round.radius": ScdmCapabilityDefinition(
key="round.radius",
display_name="圆角半径",
object_types=("round", "fillet"),
value_kind="number",
current_fields=("geometry.radius",),
default_intent="修改圆角半径",
backend_operation="change_round_radius",
post_check="target_round_radius",
required_backend_command_groups=(("ConstantRound",),),
productized=False,
roadmap_stage="S7.4",
block_reason="圆角半径属于 S7 第四批能力,真实 SCDM 命令和 STEP 样例回测尚未完成。",
),
"chamfer.distance": ScdmCapabilityDefinition(
key="chamfer.distance",
display_name="倒角距离",
object_types=("chamfer",),
value_kind="number",
current_fields=("geometry.distance", "geometry.offset"),
default_intent="修改倒角距离",
backend_operation="change_chamfer_distance",
post_check="target_chamfer_distance",
required_backend_command_groups=(("Chamfer",),),
productized=False,
roadmap_stage="S7.4",
block_reason="倒角距离属于 S7 第四批能力,真实 SCDM 命令和 STEP 样例回测尚未完成。",
),
"feature.delete_round_or_chamfer": ScdmCapabilityDefinition(
key="feature.delete_round_or_chamfer",
display_name="删除圆角/倒角",
object_types=("round", "fillet", "chamfer"),
value_kind="command",
current_fields=("1",),
default_intent="删除圆角/倒角并补面",
backend_operation="delete_round_or_chamfer",
post_check="target_feature_removed",
required_backend_command_groups=(("Fill",), ("Delete",)),
productized=False,
roadmap_stage="S7.4",
block_reason="删除圆角/倒角属于 S7 第四批能力,真实 SCDM 命令和 STEP 样例回测尚未完成。",
),
"pattern.spacing": ScdmCapabilityDefinition(
key="pattern.spacing",
display_name="阵列间距",
object_types=("pattern", "linear_pattern"),
value_kind="number",
current_fields=("geometry.spacing", "geometry.pitch"),
default_intent="修改阵列间距",
backend_operation="change_pattern_spacing",
post_check="target_pattern_spacing",
productized=False,
roadmap_stage="S7.5",
block_reason="阵列间距属于 S7 第五批能力,真实 SCDM 命令和 STEP 样例回测尚未完成。",
),
"pattern.instance_position": ScdmCapabilityDefinition(
key="pattern.instance_position",
display_name="阵列实例位置",
object_types=("pattern", "linear_pattern"),
value_kind="vector3",
current_fields=("geometry.instanceCenter", "geometry.center"),
default_intent="移动阵列实例",
backend_operation="move_pattern_instance",
post_check="target_pattern_instance_center",
productized=False,
roadmap_stage="S7.5",
block_reason="阵列实例位置属于 S7 第五批能力,真实 SCDM 命令和 STEP 样例回测尚未完成。",
),
"shell.thickness": ScdmCapabilityDefinition(
key="shell.thickness",
display_name="壳体厚度",
object_types=("shell", "thin_wall"),
value_kind="number",
current_fields=("geometry.thickness",),
default_intent="修改壳体厚度",
backend_operation="change_shell_thickness",
post_check="target_shell_thickness",
productized=False,
roadmap_stage="S7.5",
block_reason="壳体厚度属于 S7 第五批能力,真实 SCDM 命令和 STEP 样例回测尚未完成。",
),
}
def capability_definition(key: str) -> ScdmCapabilityDefinition | None:
return CAPABILITY_DEFINITIONS.get(key)
def productized_capability_keys(raw_object: Mapping[str, object]) -> tuple[str, ...]:
return tuple(
key
for key in capability_keys_for_raw_object(raw_object, include_planned=False)
if (definition := capability_definition(key)) is not None and definition.productized
)
def planned_capability_keys(raw_object: Mapping[str, object]) -> tuple[str, ...]:
return tuple(
key
for key in capability_keys_for_raw_object(raw_object, include_planned=True)
if (definition := capability_definition(key)) is not None and not definition.productized
)
def capability_keys_for_raw_object(raw_object: Mapping[str, object], *, include_planned: bool = False) -> tuple[str, ...]:
object_type = str(raw_object.get("objectType") or "").strip().lower()
geometry = _mapping(raw_object.get("geometry"))
commands = tuple(_command_operations(raw_object.get("backendCommandCandidates")))
keys: list[str] = []
if object_type in {"hole", "cylindrical_hole", "cylindrical_face_group"}:
if _has_any(geometry, ("diameter", "radius")) or _has_command_token(commands, ("diameter", "radius")):
keys.append("hole.diameter")
if _has_any(geometry, ("center", "axisCenter")) or _has_command_token(commands, ("move", "position", "translate")):
keys.append("hole.position")
if _has_command_token(commands, ("fill", "delete", "remove")):
keys.append("feature.fill")
surface_type = str(geometry.get("surfaceType") or geometry.get("surface") or "").strip().lower()
if object_type in {"face", "planar_face"} and surface_type in {"plane", "planar", ""}:
if _has_command_token(commands, ("pull", "offset", "move_face")) or _has_any(geometry, ("normal", "planeOffset")):
keys.append("face.offset")
if object_type in {"hole", "small_feature"} and _has_command_token(commands, ("fill", "delete", "remove")):
keys.append("feature.fill")
if object_type in {"slot", "obround_slot", "rectangular_slot"}:
if _has_any(geometry, ("width",)) or _has_command_token(commands, ("slot_width", "width")):
keys.append("slot.width")
if _has_any(geometry, ("depth",)) or _has_command_token(commands, ("slot_depth", "depth")):
keys.append("slot.depth")
if _has_any(geometry, ("center", "axisCenter")) or _has_command_token(commands, ("move", "position", "translate")):
keys.append("slot.position")
if object_type in {"boss", "cylindrical_boss", "rectangular_boss"}:
if _has_any(geometry, ("height",)) or _has_command_token(commands, ("boss_height", "height")):
keys.append("boss.height")
if object_type != "rectangular_boss" and (_has_any(geometry, ("diameter", "radius")) or _has_command_token(commands, ("diameter", "radius"))):
keys.append("boss.diameter")
if _has_any(geometry, ("center", "axisCenter")) or _has_command_token(commands, ("move", "position", "translate")):
keys.append("boss.position")
if object_type in {"round", "fillet"}:
if _has_any(geometry, ("radius",)) or _has_command_token(commands, ("round_radius", "fillet_radius", "radius")):
keys.append("round.radius")
if _has_command_token(commands, ("fill", "delete", "remove")):
keys.append("feature.delete_round_or_chamfer")
if object_type == "chamfer":
if _has_any(geometry, ("distance", "offset")) or _has_command_token(commands, ("chamfer_distance", "distance", "offset")):
keys.append("chamfer.distance")
if _has_command_token(commands, ("fill", "delete", "remove")):
keys.append("feature.delete_round_or_chamfer")
if object_type in {"pattern", "linear_pattern"}:
if _has_any(geometry, ("spacing", "pitch")) or _has_command_token(commands, ("pattern_spacing", "spacing", "pitch")):
keys.append("pattern.spacing")
if _has_any(geometry, ("instanceCenter", "center")) or _has_command_token(commands, ("move_instance", "instance_position")):
keys.append("pattern.instance_position")
if object_type in {"shell", "thin_wall"}:
if _has_any(geometry, ("thickness",)) or _has_command_token(commands, ("shell_thickness", "thickness")):
keys.append("shell.thickness")
result = []
for key in keys:
definition = capability_definition(key)
if definition is None:
continue
if definition.productized or include_planned:
result.append(key)
return tuple(dict.fromkeys(result))
def _mapping(value: object) -> Mapping[str, object]:
return value if isinstance(value, Mapping) else {}
def _has_any(mapping: Mapping[str, object], names: tuple[str, ...]) -> bool:
return any(name in mapping and mapping.get(name) is not None for name in names)
def _command_operations(value: object) -> list[str]:
if not isinstance(value, list):
return []
result: list[str] = []
for item in value:
if not isinstance(item, Mapping):
continue
enabled = item.get("enabled")
if enabled is False:
continue
text = " ".join(
str(part or "")
for part in (
item.get("key"),
item.get("operation"),
item.get("command"),
item.get("type"),
)
)
result.append(text.lower())
return result
def _has_command_token(commands: tuple[str, ...], tokens: tuple[str, ...]) -> bool:
return any(token in command for command in commands for token in tokens)
__all__ = [
"CAPABILITY_DEFINITIONS",
"ScdmCapabilityDefinition",
"capability_keys_for_raw_object",
"capability_definition",
"planned_capability_keys",
"productized_capability_keys",
]