feat: 接入 SCDM 优先编辑闭环并支持阵列局部间距
接入 SCDM probe/edit/cache/校验链路,增强孔组、阵列、关系式和参数表交互。 支持阵列相邻段间距、移动意图切换、结果回滚校验,并补充对应回归脚本。
This commit is contained in:
@@ -168,19 +168,40 @@ def main() -> int:
|
||||
"change_hole_diameter",
|
||||
"move_hole_axis",
|
||||
"move_slot",
|
||||
"change_slot_width",
|
||||
"change_slot_depth",
|
||||
"move_boss",
|
||||
"change_boss_height",
|
||||
"change_boss_diameter",
|
||||
"change_round_radius",
|
||||
"change_chamfer_distance",
|
||||
"change_pattern_spacing",
|
||||
"change_pattern_segment_spacing",
|
||||
"segment_before",
|
||||
"segment_split",
|
||||
"pull_face_offset",
|
||||
"fill_feature",
|
||||
"delete_round_or_chamfer",
|
||||
"StandardHoles.ModifyHoleRadius",
|
||||
"ConstantRound.ModifyRadius",
|
||||
"Chamfer.ModifyDistance",
|
||||
"OffsetFaces.Execute",
|
||||
"Move.Translate",
|
||||
"MoveOptions",
|
||||
"ConstantRoundOptions",
|
||||
"ChamferOptions",
|
||||
"OffsetFaceOptions",
|
||||
"FillOptions",
|
||||
"FillMode",
|
||||
"Delete.Execute",
|
||||
"DocumentSave",
|
||||
"scdmFaceLocators",
|
||||
"heightFaceLocators",
|
||||
"depthFaceLocators",
|
||||
"diameterFaceLocators",
|
||||
"patternInstances",
|
||||
"bodyLocators",
|
||||
"instanceKind",
|
||||
"result.json",
|
||||
"error.json",
|
||||
):
|
||||
@@ -197,6 +218,10 @@ def main() -> int:
|
||||
"faceOrdinals": [30, 31, 32],
|
||||
"globalFaceOrdinal": 40,
|
||||
"globalFaceOrdinals": [40, 41, 42],
|
||||
"width": 2.0,
|
||||
"depth": 1.5,
|
||||
"depthAxis": [0.0, 0.0, -1.0],
|
||||
"depthFaceLocators": [{"bodyIndex": 0, "faceOrdinal": 32, "globalFaceOrdinal": 42}],
|
||||
"center": [1.0, 2.0, 3.0],
|
||||
"axis": [1.0, 0.0, 0.0],
|
||||
}
|
||||
@@ -214,6 +239,37 @@ def main() -> int:
|
||||
_assert(slot_job.get("target", {}).get("backendOperation") == "move_slot", f"slot.position should route to move_slot: {slot_job}")
|
||||
_assert(slot_job.get("target", {}).get("value") == [1.0, 2.0, 5.0], f"slot.position target should be vector3: {slot_job}")
|
||||
_assert(slot_job.get("object", {}).get("geometrySignature", {}).get("faceIds") == [30, 31, 32], f"slot faces should be preserved: {slot_job}")
|
||||
slot_width_prepared = prepare_scdm_edit_job(
|
||||
step_path,
|
||||
output_dir=root / "slot-width-prepared",
|
||||
backend=backend,
|
||||
capability_key="slot.width",
|
||||
target_value="2.5",
|
||||
object_id="slot:30-31-32",
|
||||
object_signature=slot_signature,
|
||||
)
|
||||
_assert(slot_width_prepared.get("ok") is True, f"slot.width should be productized and prepare an edit job: {slot_width_prepared}")
|
||||
slot_width_job = read_json(slot_width_prepared["job_path"])
|
||||
_assert(slot_width_job.get("target", {}).get("backendOperation") == "change_slot_width", f"slot.width should route to change_slot_width: {slot_width_job}")
|
||||
_assert(slot_width_job.get("target", {}).get("value") == 2.5, f"slot.width target should be numeric: {slot_width_job}")
|
||||
_assert(slot_width_job.get("object", {}).get("geometrySignature", {}).get("width") == 2.0, f"slot width signature should be preserved: {slot_width_job}")
|
||||
slot_depth_prepared = prepare_scdm_edit_job(
|
||||
step_path,
|
||||
output_dir=root / "slot-depth-prepared",
|
||||
backend=backend,
|
||||
capability_key="slot.depth",
|
||||
target_value="2.0",
|
||||
object_id="slot:30-31-32",
|
||||
object_signature=slot_signature,
|
||||
)
|
||||
_assert(slot_depth_prepared.get("ok") is True, f"slot.depth should be productized and prepare an edit job: {slot_depth_prepared}")
|
||||
slot_depth_job = read_json(slot_depth_prepared["job_path"])
|
||||
_assert(slot_depth_job.get("target", {}).get("backendOperation") == "change_slot_depth", f"slot.depth should route to change_slot_depth: {slot_depth_job}")
|
||||
_assert(slot_depth_job.get("target", {}).get("value") == 2.0, f"slot.depth target should be numeric: {slot_depth_job}")
|
||||
slot_depth_signature = slot_depth_job.get("object", {}).get("geometrySignature", {})
|
||||
_assert(slot_depth_signature.get("depth") == 1.5, f"slot depth signature should be preserved: {slot_depth_job}")
|
||||
_assert(slot_depth_signature.get("depthFaceLocators"), f"slot depth bottom face locator should be preserved: {slot_depth_job}")
|
||||
_assert(slot_depth_signature.get("depthAxis") == [0.0, 0.0, -1.0], f"slot depth axis should be preserved: {slot_depth_job}")
|
||||
|
||||
boss_signature = {
|
||||
"objectType": "cylindrical_boss",
|
||||
@@ -223,6 +279,10 @@ def main() -> int:
|
||||
"faceOrdinals": [50, 51, 52],
|
||||
"globalFaceOrdinal": 70,
|
||||
"globalFaceOrdinals": [70, 71, 72],
|
||||
"height": 4.0,
|
||||
"diameter": 3.0,
|
||||
"heightFaceLocators": [{"bodyIndex": 0, "faceOrdinal": 52, "globalFaceOrdinal": 72}],
|
||||
"diameterFaceLocators": [{"bodyIndex": 0, "faceOrdinal": 50, "globalFaceOrdinal": 70}],
|
||||
"center": [0.0, 0.0, 2.0],
|
||||
"axis": [0.0, 0.0, 1.0],
|
||||
}
|
||||
@@ -240,12 +300,369 @@ def main() -> int:
|
||||
_assert(boss_job.get("target", {}).get("backendOperation") == "move_boss", f"boss.position should route to move_boss: {boss_job}")
|
||||
_assert(boss_job.get("target", {}).get("value") == [2.0, 0.0, 2.0], f"boss.position target should be vector3: {boss_job}")
|
||||
_assert(boss_job.get("object", {}).get("geometrySignature", {}).get("faceIds") == [50, 51, 52], f"boss faces should be preserved: {boss_job}")
|
||||
boss_height_prepared = prepare_scdm_edit_job(
|
||||
step_path,
|
||||
output_dir=root / "boss-height-prepared",
|
||||
backend=backend,
|
||||
capability_key="boss.height",
|
||||
target_value="5.5",
|
||||
object_id="boss:50-51-52",
|
||||
object_signature=boss_signature,
|
||||
)
|
||||
_assert(boss_height_prepared.get("ok") is True, f"boss.height should be productized and prepare an edit job: {boss_height_prepared}")
|
||||
boss_height_job = read_json(boss_height_prepared["job_path"])
|
||||
_assert(boss_height_job.get("target", {}).get("backendOperation") == "change_boss_height", f"boss.height should route to change_boss_height: {boss_height_job}")
|
||||
_assert(boss_height_job.get("target", {}).get("value") == 5.5, f"boss.height target should be numeric: {boss_height_job}")
|
||||
boss_height_signature = boss_height_job.get("object", {}).get("geometrySignature", {})
|
||||
_assert(boss_height_signature.get("height") == 4.0, f"boss height signature should be preserved: {boss_height_job}")
|
||||
_assert(boss_height_signature.get("heightFaceLocators"), f"boss height top face locator should be preserved: {boss_height_job}")
|
||||
boss_diameter_prepared = prepare_scdm_edit_job(
|
||||
step_path,
|
||||
output_dir=root / "boss-diameter-prepared",
|
||||
backend=backend,
|
||||
capability_key="boss.diameter",
|
||||
target_value="4.5",
|
||||
object_id="boss:50-51-52",
|
||||
object_signature=boss_signature,
|
||||
)
|
||||
_assert(boss_diameter_prepared.get("ok") is True, f"boss.diameter should be productized and prepare an edit job: {boss_diameter_prepared}")
|
||||
boss_diameter_job = read_json(boss_diameter_prepared["job_path"])
|
||||
_assert(boss_diameter_job.get("target", {}).get("backendOperation") == "change_boss_diameter", f"boss.diameter should route to change_boss_diameter: {boss_diameter_job}")
|
||||
_assert(boss_diameter_job.get("target", {}).get("value") == 4.5, f"boss.diameter target should be numeric: {boss_diameter_job}")
|
||||
boss_diameter_signature = boss_diameter_job.get("object", {}).get("geometrySignature", {})
|
||||
_assert(boss_diameter_signature.get("diameter") == 3.0, f"boss diameter signature should be preserved: {boss_diameter_job}")
|
||||
_assert(boss_diameter_signature.get("diameterFaceLocators"), f"boss diameter side face locator should be preserved: {boss_diameter_job}")
|
||||
|
||||
round_signature = {
|
||||
"objectType": "round",
|
||||
"faceIds": [60],
|
||||
"bodyIndex": 0,
|
||||
"faceOrdinal": 60,
|
||||
"globalFaceOrdinal": 80,
|
||||
"center": [1.0, 0.0, 2.0],
|
||||
"axis": [0.0, 0.0, 1.0],
|
||||
"radius": 0.5,
|
||||
"roundType": "ConstantRound",
|
||||
"isConstantRound": True,
|
||||
}
|
||||
round_radius_prepared = prepare_scdm_edit_job(
|
||||
step_path,
|
||||
output_dir=root / "round-radius-prepared",
|
||||
backend=backend,
|
||||
capability_key="round.radius",
|
||||
target_value="0.75",
|
||||
object_id="round:60",
|
||||
object_signature=round_signature,
|
||||
)
|
||||
_assert(round_radius_prepared.get("ok") is True, f"round.radius should be productized and prepare an edit job: {round_radius_prepared}")
|
||||
round_radius_job = read_json(round_radius_prepared["job_path"])
|
||||
_assert(round_radius_job.get("target", {}).get("backendOperation") == "change_round_radius", f"round.radius should route to change_round_radius: {round_radius_job}")
|
||||
_assert(round_radius_job.get("target", {}).get("value") == 0.75, f"round.radius target should be numeric: {round_radius_job}")
|
||||
round_radius_signature = round_radius_job.get("object", {}).get("geometrySignature", {})
|
||||
_assert(round_radius_signature.get("isConstantRound") is True, f"round.radius should preserve constant round evidence: {round_radius_job}")
|
||||
_assert(round_radius_signature.get("radius") == 0.5, f"round.radius should preserve current radius: {round_radius_job}")
|
||||
round_delete_prepared = prepare_scdm_edit_job(
|
||||
step_path,
|
||||
output_dir=root / "round-delete-prepared",
|
||||
backend=backend,
|
||||
capability_key="feature.delete_round_or_chamfer",
|
||||
target_value="",
|
||||
object_id="round:60",
|
||||
object_signature=round_signature,
|
||||
)
|
||||
_assert(round_delete_prepared.get("ok") is True, f"round/chamfer delete should be productized and prepare an edit job: {round_delete_prepared}")
|
||||
round_delete_job = read_json(round_delete_prepared["job_path"])
|
||||
_assert(round_delete_job.get("target", {}).get("backendOperation") == "delete_round_or_chamfer", f"round delete should route to delete_round_or_chamfer: {round_delete_job}")
|
||||
_assert(round_delete_job.get("target", {}).get("value") is True, f"command target should be true: {round_delete_job}")
|
||||
|
||||
chamfer_signature = {
|
||||
"objectType": "chamfer",
|
||||
"faceIds": [63],
|
||||
"bodyIndex": 0,
|
||||
"faceOrdinal": 63,
|
||||
"globalFaceOrdinal": 83,
|
||||
"distance": 0.8,
|
||||
"distance1": 0.8,
|
||||
"distance2": 0.8,
|
||||
"chamferType": "EqualDistanceChamfer",
|
||||
"isEqualDistanceChamfer": True,
|
||||
}
|
||||
chamfer_distance_prepared = prepare_scdm_edit_job(
|
||||
step_path,
|
||||
output_dir=root / "chamfer-distance-prepared",
|
||||
backend=backend,
|
||||
capability_key="chamfer.distance",
|
||||
target_value="1.2",
|
||||
object_id="chamfer:63",
|
||||
object_signature=chamfer_signature,
|
||||
)
|
||||
_assert(chamfer_distance_prepared.get("ok") is True, f"chamfer.distance should be productized and prepare an edit job: {chamfer_distance_prepared}")
|
||||
chamfer_distance_job = read_json(chamfer_distance_prepared["job_path"])
|
||||
_assert(chamfer_distance_job.get("target", {}).get("backendOperation") == "change_chamfer_distance", f"chamfer.distance should route to change_chamfer_distance: {chamfer_distance_job}")
|
||||
_assert(chamfer_distance_job.get("target", {}).get("value") == 1.2, f"chamfer.distance target should be numeric: {chamfer_distance_job}")
|
||||
chamfer_job_signature = chamfer_distance_job.get("object", {}).get("geometrySignature", {})
|
||||
_assert(chamfer_job_signature.get("isEqualDistanceChamfer") is True, f"chamfer.distance should preserve equal-distance evidence: {chamfer_distance_job}")
|
||||
_assert(chamfer_job_signature.get("distance") == 0.8, f"chamfer.distance should preserve current distance: {chamfer_distance_job}")
|
||||
|
||||
pattern_signature = {
|
||||
"objectType": "linear_pattern",
|
||||
"faceIds": [85, 94, 87, 96, 89, 98],
|
||||
"bodyIndex": 0,
|
||||
"axis": [1.0, 0.0, 0.0],
|
||||
"spacing": 5.0,
|
||||
"pitch": 5.0,
|
||||
"instanceCount": 3,
|
||||
"instanceCenters": [[0.0, 0.0, 0.0], [5.0, 0.0, 0.0], [10.0, 0.0, 0.0]],
|
||||
"patternInstances": [
|
||||
{
|
||||
"sourceObjectId": "hole:a",
|
||||
"center": [0.0, 0.0, 0.0],
|
||||
"faceIds": [85, 94],
|
||||
"scdmFaceLocators": [
|
||||
{"bodyIndex": 0, "faceOrdinal": 12, "globalFaceOrdinal": 85},
|
||||
{"bodyIndex": 0, "faceOrdinal": 19, "globalFaceOrdinal": 94},
|
||||
],
|
||||
},
|
||||
{
|
||||
"sourceObjectId": "hole:b",
|
||||
"center": [5.0, 0.0, 0.0],
|
||||
"faceIds": [87, 96],
|
||||
"scdmFaceLocators": [
|
||||
{"bodyIndex": 0, "faceOrdinal": 22, "globalFaceOrdinal": 87},
|
||||
{"bodyIndex": 0, "faceOrdinal": 29, "globalFaceOrdinal": 96},
|
||||
],
|
||||
},
|
||||
{
|
||||
"sourceObjectId": "hole:c",
|
||||
"center": [10.0, 0.0, 0.0],
|
||||
"faceIds": [89, 98],
|
||||
"scdmFaceLocators": [
|
||||
{"bodyIndex": 0, "faceOrdinal": 32, "globalFaceOrdinal": 89},
|
||||
{"bodyIndex": 0, "faceOrdinal": 39, "globalFaceOrdinal": 98},
|
||||
],
|
||||
},
|
||||
],
|
||||
}
|
||||
pattern_spacing_prepared = prepare_scdm_edit_job(
|
||||
step_path,
|
||||
output_dir=root / "pattern-spacing-prepared",
|
||||
backend=backend,
|
||||
capability_key="pattern.spacing",
|
||||
target_value="7.5",
|
||||
object_id="pattern:holes-a-b-c",
|
||||
object_signature=pattern_signature,
|
||||
)
|
||||
_assert(pattern_spacing_prepared.get("ok") is True, f"pattern.spacing should be productized and prepare an edit job: {pattern_spacing_prepared}")
|
||||
pattern_spacing_job = read_json(pattern_spacing_prepared["job_path"])
|
||||
_assert(pattern_spacing_job.get("target", {}).get("backendOperation") == "change_pattern_spacing", f"pattern.spacing should route to change_pattern_spacing: {pattern_spacing_job}")
|
||||
_assert(pattern_spacing_job.get("target", {}).get("value") == 7.5, f"pattern.spacing target should be numeric: {pattern_spacing_job}")
|
||||
pattern_job_signature = pattern_spacing_job.get("object", {}).get("geometrySignature", {})
|
||||
_assert(pattern_job_signature.get("spacing") == 5.0, f"pattern spacing signature should be preserved: {pattern_spacing_job}")
|
||||
_assert(len(pattern_job_signature.get("patternInstances") or []) == 3, f"pattern instance locators should be preserved: {pattern_spacing_job}")
|
||||
pattern_segment_signature = dict(pattern_signature)
|
||||
pattern_segment_signature["segmentIndex"] = 1
|
||||
pattern_segment_signature["movingSide"] = "after"
|
||||
pattern_segment_prepared = prepare_scdm_edit_job(
|
||||
step_path,
|
||||
output_dir=root / "pattern-segment-spacing-prepared",
|
||||
backend=backend,
|
||||
capability_key="pattern.segment_spacing",
|
||||
target_value="6.5",
|
||||
object_id="pattern:holes-a-b-c",
|
||||
object_signature=pattern_segment_signature,
|
||||
)
|
||||
_assert(pattern_segment_prepared.get("ok") is True, f"pattern.segment_spacing should prepare an edit job: {pattern_segment_prepared}")
|
||||
pattern_segment_job = read_json(pattern_segment_prepared["job_path"])
|
||||
_assert(
|
||||
pattern_segment_job.get("target", {}).get("backendOperation") == "change_pattern_segment_spacing",
|
||||
f"pattern.segment_spacing should route to change_pattern_segment_spacing: {pattern_segment_job}",
|
||||
)
|
||||
_assert(pattern_segment_job.get("target", {}).get("value") == 6.5, f"pattern.segment_spacing target should be numeric: {pattern_segment_job}")
|
||||
_assert(
|
||||
pattern_segment_job.get("object", {}).get("geometrySignature", {}).get("segmentIndex") == 1,
|
||||
f"pattern.segment_spacing should preserve the selected adjacent segment: {pattern_segment_job}",
|
||||
)
|
||||
pattern_segment_before_signature = dict(pattern_segment_signature)
|
||||
pattern_segment_before_signature["movingSide"] = "before"
|
||||
pattern_segment_before_prepared = prepare_scdm_edit_job(
|
||||
step_path,
|
||||
output_dir=root / "pattern-segment-spacing-before-prepared",
|
||||
backend=backend,
|
||||
capability_key="pattern.segment_spacing",
|
||||
target_value="6.5",
|
||||
object_id="pattern:holes-a-b-c",
|
||||
object_signature=pattern_segment_before_signature,
|
||||
)
|
||||
_assert(
|
||||
pattern_segment_before_prepared.get("ok") is True
|
||||
and read_json(pattern_segment_before_prepared["job_path"]).get("object", {}).get("geometrySignature", {}).get("movingSide") == "before",
|
||||
f"pattern.segment_spacing should preserve fix-right/move-left semantics: {pattern_segment_before_prepared}",
|
||||
)
|
||||
pattern_segment_split_signature = dict(pattern_segment_signature)
|
||||
pattern_segment_split_signature["movingSide"] = "split"
|
||||
pattern_segment_split_prepared = prepare_scdm_edit_job(
|
||||
step_path,
|
||||
output_dir=root / "pattern-segment-spacing-split-prepared",
|
||||
backend=backend,
|
||||
capability_key="pattern.segment_spacing",
|
||||
target_value="6.5",
|
||||
object_id="pattern:holes-a-b-c",
|
||||
object_signature=pattern_segment_split_signature,
|
||||
)
|
||||
_assert(
|
||||
pattern_segment_split_prepared.get("ok") is True
|
||||
and read_json(pattern_segment_split_prepared["job_path"]).get("object", {}).get("geometrySignature", {}).get("movingSide") == "split",
|
||||
f"pattern.segment_spacing should preserve split/keep-center semantics: {pattern_segment_split_prepared}",
|
||||
)
|
||||
|
||||
body_pattern_signature = {
|
||||
"objectType": "linear_pattern",
|
||||
"patternKind": "body",
|
||||
"instanceKind": "body",
|
||||
"faceIds": [300, 301, 302],
|
||||
"bodyIndices": [20, 21, 22],
|
||||
"axis": [0.0, 1.0, 0.0],
|
||||
"spacing": 5.0,
|
||||
"pitch": 5.0,
|
||||
"instanceCount": 3,
|
||||
"instanceCenters": [[0.0, 20.0, 0.0], [0.0, 25.0, 0.0], [0.0, 30.0, 0.0]],
|
||||
"patternInstances": [
|
||||
{"sourceObjectId": "body:20", "instanceKind": "body", "center": [0.0, 20.0, 0.0], "bodyIndex": 20, "bodyLocators": [{"bodyIndex": 20}], "faceIds": [300]},
|
||||
{"sourceObjectId": "body:21", "instanceKind": "body", "center": [0.0, 25.0, 0.0], "bodyIndex": 21, "bodyLocators": [{"bodyIndex": 21}], "faceIds": [301]},
|
||||
{"sourceObjectId": "body:22", "instanceKind": "body", "center": [0.0, 30.0, 0.0], "bodyIndex": 22, "bodyLocators": [{"bodyIndex": 22}], "faceIds": [302]},
|
||||
],
|
||||
}
|
||||
body_pattern_prepared = prepare_scdm_edit_job(
|
||||
step_path,
|
||||
output_dir=root / "body-pattern-spacing-prepared",
|
||||
backend=backend,
|
||||
capability_key="pattern.spacing",
|
||||
target_value="8",
|
||||
object_id="pattern:parts-20-22",
|
||||
object_signature=body_pattern_signature,
|
||||
)
|
||||
_assert(
|
||||
body_pattern_prepared.get("ok") is False
|
||||
and body_pattern_prepared.get("reason") == "body-pattern-spacing-missing-component-locators",
|
||||
f"body pattern spacing should be blocked until component occurrence locators are available: {body_pattern_prepared}",
|
||||
)
|
||||
body_segment_pattern_signature = dict(body_pattern_signature)
|
||||
body_segment_pattern_signature["segmentIndex"] = 0
|
||||
body_segment_prepared = prepare_scdm_edit_job(
|
||||
step_path,
|
||||
output_dir=root / "body-pattern-segment-spacing-prepared",
|
||||
backend=backend,
|
||||
capability_key="pattern.segment_spacing",
|
||||
target_value="6",
|
||||
object_id="pattern:parts-20-22",
|
||||
object_signature=body_segment_pattern_signature,
|
||||
)
|
||||
_assert(
|
||||
body_segment_prepared.get("ok") is False
|
||||
and body_segment_prepared.get("reason") == "body-pattern-spacing-missing-component-locators",
|
||||
f"body pattern local segment spacing should also require component occurrence locators: {body_segment_prepared}",
|
||||
)
|
||||
component_body_pattern_signature = dict(body_pattern_signature)
|
||||
component_body_pattern_signature["componentInstanceCount"] = 3
|
||||
component_body_pattern_signature["patternInstances"] = [
|
||||
{
|
||||
**dict(item),
|
||||
"componentLocators": [
|
||||
{
|
||||
"componentIndex": index,
|
||||
"componentPath": [index],
|
||||
"componentBodyIndex": 0,
|
||||
"bodyIndex": item.get("bodyIndex"),
|
||||
"componentName": f"Part {index + 1}",
|
||||
}
|
||||
],
|
||||
}
|
||||
for index, item in enumerate(body_pattern_signature["patternInstances"])
|
||||
]
|
||||
component_body_pattern_prepared = prepare_scdm_edit_job(
|
||||
step_path,
|
||||
output_dir=root / "component-body-pattern-spacing-prepared",
|
||||
backend=backend,
|
||||
capability_key="pattern.spacing",
|
||||
target_value="8",
|
||||
object_id="pattern:components-20-22",
|
||||
object_signature=component_body_pattern_signature,
|
||||
)
|
||||
_assert(
|
||||
component_body_pattern_prepared.get("ok") is True,
|
||||
f"body pattern spacing should prepare when every member has a component occurrence locator: {component_body_pattern_prepared}",
|
||||
)
|
||||
component_body_pattern_job = read_json(component_body_pattern_prepared["job_path"])
|
||||
component_instances = component_body_pattern_job.get("object", {}).get("geometrySignature", {}).get("patternInstances") or []
|
||||
_assert(
|
||||
all(item.get("componentLocators") for item in component_instances if isinstance(item, dict)),
|
||||
f"component occurrence locators should be preserved in pattern.spacing job: {component_body_pattern_job}",
|
||||
)
|
||||
component_body_segment_signature = dict(component_body_pattern_signature)
|
||||
component_body_segment_signature["segmentIndex"] = 0
|
||||
component_body_segment_prepared = prepare_scdm_edit_job(
|
||||
step_path,
|
||||
output_dir=root / "component-body-pattern-segment-spacing-prepared",
|
||||
backend=backend,
|
||||
capability_key="pattern.segment_spacing",
|
||||
target_value="6",
|
||||
object_id="pattern:components-20-22",
|
||||
object_signature=component_body_segment_signature,
|
||||
)
|
||||
_assert(
|
||||
component_body_segment_prepared.get("ok") is True,
|
||||
f"body pattern local segment spacing should prepare with component occurrence locators: {component_body_segment_prepared}",
|
||||
)
|
||||
blocked_pattern_signature = dict(pattern_signature)
|
||||
blocked_pattern_signature["supportPatternFit"] = {
|
||||
"supportFaceIds": [92],
|
||||
"localUnitScale": 0.001,
|
||||
"maxSpacing": 0.0012,
|
||||
"maxSpacingLocal": 1.2,
|
||||
"instanceCount": 3,
|
||||
}
|
||||
blocked_pattern_prepared = prepare_scdm_edit_job(
|
||||
step_path,
|
||||
output_dir=root / "body-pattern-spacing-blocked",
|
||||
backend=backend,
|
||||
capability_key="pattern.spacing",
|
||||
target_value=0.002,
|
||||
object_id="pattern:holes-a-b-c",
|
||||
object_signature=blocked_pattern_signature,
|
||||
)
|
||||
_assert(
|
||||
blocked_pattern_prepared.get("ok") is False
|
||||
and blocked_pattern_prepared.get("reason") == "pattern-spacing-exceeds-support",
|
||||
f"pattern spacing should be blocked before SCDM when it exceeds support Face limits: {blocked_pattern_prepared}",
|
||||
)
|
||||
blocked_segment_signature = dict(pattern_segment_signature)
|
||||
blocked_segment_signature["supportPatternFit"] = {
|
||||
"supportFaceIds": [92],
|
||||
"localUnitScale": 0.001,
|
||||
"maxSegmentSpacing": 0.0013,
|
||||
"maxSegmentSpacingLocal": 1.3,
|
||||
"instanceCount": 3,
|
||||
}
|
||||
blocked_segment_prepared = prepare_scdm_edit_job(
|
||||
step_path,
|
||||
output_dir=root / "pattern-segment-spacing-blocked",
|
||||
backend=backend,
|
||||
capability_key="pattern.segment_spacing",
|
||||
target_value=0.002,
|
||||
object_id="pattern:holes-a-b-c",
|
||||
object_signature=blocked_segment_signature,
|
||||
)
|
||||
_assert(
|
||||
blocked_segment_prepared.get("ok") is False
|
||||
and blocked_segment_prepared.get("reason") == "pattern-spacing-exceeds-support",
|
||||
f"pattern segment spacing should be blocked before SCDM when it exceeds support Face limits: {blocked_segment_prepared}",
|
||||
)
|
||||
|
||||
planned = prepare_scdm_edit_job(
|
||||
step_path,
|
||||
output_dir=root / "unsupported",
|
||||
backend=backend,
|
||||
capability_key="slot.width",
|
||||
capability_key="pattern.instance_position",
|
||||
target_value="1",
|
||||
object_signature=signature,
|
||||
)
|
||||
@@ -305,6 +722,30 @@ def main() -> int:
|
||||
)
|
||||
_assert(slot_success.get("ok") is True, f"fake slot.position edit should succeed: {slot_success}")
|
||||
_assert(slot_success.get("backend_operation") == "move_slot", f"slot.position should report move_slot: {slot_success}")
|
||||
slot_width_success = run_scdm_edit_job(
|
||||
step_path,
|
||||
output_dir=root / "slot-width-success",
|
||||
backend=backend,
|
||||
capability_key="slot.width",
|
||||
target_value=2.5,
|
||||
object_id="slot:30-31-32",
|
||||
object_signature=slot_signature,
|
||||
runner=_successful_runner,
|
||||
)
|
||||
_assert(slot_width_success.get("ok") is True, f"fake slot.width edit should succeed: {slot_width_success}")
|
||||
_assert(slot_width_success.get("backend_operation") == "change_slot_width", f"slot.width should report change_slot_width: {slot_width_success}")
|
||||
slot_depth_success = run_scdm_edit_job(
|
||||
step_path,
|
||||
output_dir=root / "slot-depth-success",
|
||||
backend=backend,
|
||||
capability_key="slot.depth",
|
||||
target_value=2.0,
|
||||
object_id="slot:30-31-32",
|
||||
object_signature=slot_signature,
|
||||
runner=_successful_runner,
|
||||
)
|
||||
_assert(slot_depth_success.get("ok") is True, f"fake slot.depth edit should succeed: {slot_depth_success}")
|
||||
_assert(slot_depth_success.get("backend_operation") == "change_slot_depth", f"slot.depth should report change_slot_depth: {slot_depth_success}")
|
||||
|
||||
boss_success = run_scdm_edit_job(
|
||||
step_path,
|
||||
@@ -318,6 +759,97 @@ def main() -> int:
|
||||
)
|
||||
_assert(boss_success.get("ok") is True, f"fake boss.position edit should succeed: {boss_success}")
|
||||
_assert(boss_success.get("backend_operation") == "move_boss", f"boss.position should report move_boss: {boss_success}")
|
||||
boss_height_success = run_scdm_edit_job(
|
||||
step_path,
|
||||
output_dir=root / "boss-height-success",
|
||||
backend=backend,
|
||||
capability_key="boss.height",
|
||||
target_value=5.5,
|
||||
object_id="boss:50-51-52",
|
||||
object_signature=boss_signature,
|
||||
runner=_successful_runner,
|
||||
)
|
||||
_assert(boss_height_success.get("ok") is True, f"fake boss.height edit should succeed: {boss_height_success}")
|
||||
_assert(boss_height_success.get("backend_operation") == "change_boss_height", f"boss.height should report change_boss_height: {boss_height_success}")
|
||||
boss_diameter_success = run_scdm_edit_job(
|
||||
step_path,
|
||||
output_dir=root / "boss-diameter-success",
|
||||
backend=backend,
|
||||
capability_key="boss.diameter",
|
||||
target_value=4.5,
|
||||
object_id="boss:50-51-52",
|
||||
object_signature=boss_signature,
|
||||
runner=_successful_runner,
|
||||
)
|
||||
_assert(boss_diameter_success.get("ok") is True, f"fake boss.diameter edit should succeed: {boss_diameter_success}")
|
||||
_assert(boss_diameter_success.get("backend_operation") == "change_boss_diameter", f"boss.diameter should report change_boss_diameter: {boss_diameter_success}")
|
||||
|
||||
round_radius_success = run_scdm_edit_job(
|
||||
step_path,
|
||||
output_dir=root / "round-radius-success",
|
||||
backend=backend,
|
||||
capability_key="round.radius",
|
||||
target_value=0.75,
|
||||
object_id="round:60",
|
||||
object_signature=round_signature,
|
||||
runner=_successful_runner,
|
||||
)
|
||||
_assert(round_radius_success.get("ok") is True, f"fake round.radius edit should succeed: {round_radius_success}")
|
||||
_assert(round_radius_success.get("backend_operation") == "change_round_radius", f"round.radius should report change_round_radius: {round_radius_success}")
|
||||
|
||||
round_delete_success = run_scdm_edit_job(
|
||||
step_path,
|
||||
output_dir=root / "round-delete-success",
|
||||
backend=backend,
|
||||
capability_key="feature.delete_round_or_chamfer",
|
||||
target_value="",
|
||||
object_id="round:60",
|
||||
object_signature=round_signature,
|
||||
runner=_successful_runner,
|
||||
)
|
||||
_assert(round_delete_success.get("ok") is True, f"fake round/chamfer delete edit should succeed: {round_delete_success}")
|
||||
_assert(round_delete_success.get("backend_operation") == "delete_round_or_chamfer", f"round/chamfer delete should report delete_round_or_chamfer: {round_delete_success}")
|
||||
|
||||
chamfer_distance_success = run_scdm_edit_job(
|
||||
step_path,
|
||||
output_dir=root / "chamfer-distance-success",
|
||||
backend=backend,
|
||||
capability_key="chamfer.distance",
|
||||
target_value=1.2,
|
||||
object_id="chamfer:63",
|
||||
object_signature=chamfer_signature,
|
||||
runner=_successful_runner,
|
||||
)
|
||||
_assert(chamfer_distance_success.get("ok") is True, f"fake chamfer.distance edit should succeed: {chamfer_distance_success}")
|
||||
_assert(chamfer_distance_success.get("backend_operation") == "change_chamfer_distance", f"chamfer.distance should report change_chamfer_distance: {chamfer_distance_success}")
|
||||
|
||||
pattern_spacing_success = run_scdm_edit_job(
|
||||
step_path,
|
||||
output_dir=root / "pattern-spacing-success",
|
||||
backend=backend,
|
||||
capability_key="pattern.spacing",
|
||||
target_value=7.5,
|
||||
object_id="pattern:holes-a-b-c",
|
||||
object_signature=pattern_signature,
|
||||
runner=_successful_runner,
|
||||
)
|
||||
_assert(pattern_spacing_success.get("ok") is True, f"fake pattern.spacing edit should succeed: {pattern_spacing_success}")
|
||||
_assert(pattern_spacing_success.get("backend_operation") == "change_pattern_spacing", f"pattern.spacing should report change_pattern_spacing: {pattern_spacing_success}")
|
||||
pattern_segment_success = run_scdm_edit_job(
|
||||
step_path,
|
||||
output_dir=root / "pattern-segment-spacing-success",
|
||||
backend=backend,
|
||||
capability_key="pattern.segment_spacing",
|
||||
target_value=6.5,
|
||||
object_id="pattern:holes-a-b-c",
|
||||
object_signature=pattern_segment_signature,
|
||||
runner=_successful_runner,
|
||||
)
|
||||
_assert(pattern_segment_success.get("ok") is True, f"fake pattern.segment_spacing edit should succeed: {pattern_segment_success}")
|
||||
_assert(
|
||||
pattern_segment_success.get("backend_operation") == "change_pattern_segment_spacing",
|
||||
f"pattern.segment_spacing should report change_pattern_segment_spacing: {pattern_segment_success}",
|
||||
)
|
||||
|
||||
missing_output = run_scdm_edit_job(
|
||||
step_path,
|
||||
|
||||
Reference in New Issue
Block a user