This commit is contained in:
2026-06-16 11:15:03 +08:00
parent f71a48d3b8
commit f6d8829071
5 changed files with 365 additions and 59 deletions
+1 -1
View File
@@ -17,7 +17,7 @@ DEFAULT_CAM_PREFERENCES = {
"mold_steel": "P20", "mold_steel": "P20",
"surface_quality": "standard", "surface_quality": "standard",
"controller": "fanuc", "controller": "fanuc",
"include_gcode": False, "include_gcode": True,
} }
+1 -1
View File
@@ -503,7 +503,7 @@ class BaseMoldGenerator:
) )
logger.info("回退方案型腔/型芯分离完成") logger.info("回退方案型腔/型芯分离完成")
return cavity or mold_block, core return cavity, core
except Exception as e: except Exception as e:
logger.error(f"分模回退方案失败: {e}") logger.error(f"分模回退方案失败: {e}")
+340 -56
View File
@@ -244,44 +244,41 @@ class RoughingToolpathGenerator:
tool: Dict, cutting_params: Dict, tool: Dict, cutting_params: Dict,
stock_allowance: float = 0.5) -> Dict[str, Any]: stock_allowance: float = 0.5) -> Dict[str, Any]:
""" """
Z层等高粗加工 Z层等高粗加工 — 生成真实刀路坐标点。
策略:从顶面逐层向下铣削,每层切深为 axial_depth 策略:从顶面逐层向下铣削,每层在型腔边界框内往复走刀。
Args:
stock_bbox: 毛坯边界框
cavity_bbox: 型腔边界框
tool: 刀具参数
cutting_params: 切削参数
stock_allowance: 精加工余量 mm
Returns:
粗加工刀路方案
""" """
z_min = cavity_bbox.get("min", [0, 0, 0])[2] cx_min, cy_min, cz_min = cavity_bbox.get("min", [0, 0, 0])
z_max = cavity_bbox.get("max", [0, 0, 0])[2] cx_max, cy_max, cz_max = cavity_bbox.get("max", [0, 0, 0])
total_depth = z_max - z_min total_depth = cz_max - cz_min
axial_depth = cutting_params["axial_depth_mm"] axial_depth = cutting_params["axial_depth_mm"]
num_levels = max(1, math.ceil(total_depth / axial_depth)) num_levels = max(1, math.ceil(total_depth / axial_depth))
actual_depth = total_depth / num_levels actual_depth = total_depth / num_levels
stepover = cutting_params["radial_depth_mm"] stepover = cutting_params["radial_depth_mm"]
levels = [] levels = []
path_points = []
for i in range(num_levels): for i in range(num_levels):
z_level = z_max - (i + 1) * actual_depth + stock_allowance z_level = cz_max - (i + 1) * actual_depth + stock_allowance
rounded_z = round(z_level, 2)
levels.append({ levels.append({
"z": round(z_level, 2), "z": rounded_z,
"depth": round(actual_depth, 2), "depth": round(actual_depth, 2),
"level_index": i + 1, "level_index": i + 1,
}) })
layer_points = self._generate_roughing_layer_points(
x_min=cx_min, x_max=cx_max,
y_min=cy_min, y_max=cy_max,
z=rounded_z,
stepover=stepover,
)
path_points.extend(layer_points)
toolpath_length = self._estimate_roughing_length( toolpath_length = self._estimate_roughing_length(
cavity_bbox, num_levels, stepover cavity_bbox, num_levels, stepover
) )
machining_time = CuttingParamsCalculator.estimate_machining_time( machining_time = CuttingParamsCalculator.estimate_machining_time(
toolpath_length, cutting_params["feed_rate_mm_min"] toolpath_length, cutting_params["feed_rate_mm_min"]
) )
@@ -298,8 +295,35 @@ class RoughingToolpathGenerator:
"estimated_time_min": round(machining_time, 1), "estimated_time_min": round(machining_time, 1),
"approach_type": "helical_ramp", "approach_type": "helical_ramp",
"ramp_angle": 2.0, "ramp_angle": 2.0,
"path_points": path_points,
} }
@staticmethod
def _generate_roughing_layer_points(
x_min: float, x_max: float,
y_min: float, y_max: float,
z: float, stepover: float,
) -> List[List[float]]:
"""为单个 Z 层生成往复走刀坐标点序列。"""
points = []
y_positions = []
y = y_min
while y <= y_max + 0.001:
y_positions.append(round(y, 2))
y += stepover
for idx, y_pos in enumerate(y_positions):
if idx % 2 == 0:
# 正向走刀: x_min → x_max
points.append([round(x_min, 2), y_pos, z])
points.append([round(x_max, 2), y_pos, z])
else:
# 反向走刀: x_max → x_min
points.append([round(x_max, 2), y_pos, z])
points.append([round(x_min, 2), y_pos, z])
return points
def _estimate_roughing_length(self, cavity_bbox: Dict, num_levels: int, def _estimate_roughing_length(self, cavity_bbox: Dict, num_levels: int,
stepover: float) -> float: stepover: float) -> float:
"""估算粗加工刀路总长度""" """估算粗加工刀路总长度"""
@@ -313,6 +337,70 @@ class RoughingToolpathGenerator:
return length_per_level * num_levels return length_per_level * num_levels
def generate_core_roughing(self, stock_bbox: Dict, core_bbox: Dict,
tool: Dict, cutting_params: Dict,
stock_allowance: float = 0.5) -> Dict[str, Any]:
"""
型芯粗加工 — 从外轮廓向内逐层等高铣削。
型芯是凸出的几何体,策略是从毛坯外边界向内逐层收缩切削,
每层保持同一 Z 高度,环绕型芯轮廓走刀。
"""
sx_min, sy_min, sz_min = stock_bbox.get("min", [0, 0, 0])
sx_max, sy_max, sz_max = stock_bbox.get("max", [0, 0, 0])
cx_min, cy_min, cz_min = core_bbox.get("min", [0, 0, 0])
cx_max, cy_max, cz_max = core_bbox.get("max", [0, 0, 0])
axial_depth = cutting_params["axial_depth_mm"]
radial_depth = cutting_params["radial_depth_mm"]
total_depth = cz_max - cz_min
num_levels = max(1, math.ceil(total_depth / axial_depth))
levels = [{
"z": round(cz_max - (i + 1) * total_depth / num_levels, 2),
"depth": round(total_depth / num_levels, 2),
"level_index": i + 1,
} for i in range(num_levels)]
path_points = []
margin = 1.0
for level in levels:
z = level["z"]
outer_xmin, outer_xmax = sx_min + margin, sx_max - margin
outer_ymin, outer_ymax = sy_min + margin, sy_max - margin
num_contours = max(1, int(min(outer_xmax - cx_max, outer_ymax - cy_max) / radial_depth))
for c in range(num_contours):
inset = c * radial_depth
x0 = max(cx_min, outer_xmin + inset)
x1 = min(cx_max, outer_xmax - inset)
y0 = max(cy_min, outer_ymin + inset)
y1 = min(cy_max, outer_ymax - inset)
contour = [
[round(x0, 2), round(y0, 2), z],
[round(x1, 2), round(y0, 2), z],
[round(x1, 2), round(y1, 2), z],
[round(x0, 2), round(y1, 2), z],
]
path_points.extend(contour)
dims = core_bbox.get("dimensions", [100, 100, 50])
toolpath_length = num_levels * 2 * (dims[0] + dims[1]) * 2.0
machining_time = CuttingParamsCalculator.estimate_machining_time(
toolpath_length, cutting_params["feed_rate_mm_min"]
)
return {
"strategy": "core_roughing",
"tool": cutting_params,
"levels": levels,
"num_levels": num_levels,
"stepover": radial_depth,
"stock_allowance": stock_allowance,
"estimated_toolpath_length": round(toolpath_length, 1),
"estimated_time_min": round(machining_time, 1),
"path_points": path_points,
}
class FinishingToolpathGenerator: class FinishingToolpathGenerator:
"""精加工刀路生成器""" """精加工刀路生成器"""
@@ -322,30 +410,36 @@ class FinishingToolpathGenerator:
stepover: float = 0.3, stepover: float = 0.3,
angle: float = 0.0) -> Dict[str, Any]: angle: float = 0.0) -> Dict[str, Any]:
""" """
平行铣削精加工 平行铣削精加工 — 生成真实刀路坐标点。
Args: 在型腔顶部沿 Y 方向往复走刀,覆盖整个型腔 XY 区域。
cavity_bbox: 型腔边界框
tool: 刀具参数
cutting_params: 切削参数
stepover: 步距 mm
angle: 加工角度
Returns:
精加工刀路方案
""" """
dims = cavity_bbox.get("dimensions", [100, 100, 50]) dims = cavity_bbox.get("dimensions", [100, 100, 50])
cx_min, cy_min, cz_min = cavity_bbox.get("min", [0, 0, 0])
cx_max, cy_max, cz_max = cavity_bbox.get("max", [0, 0, 0])
width = dims[0] width = dims[0]
length = dims[1] length = dims[1]
finish_z = cz_max - 0.1 # 型腔顶面
num_passes = max(1, int(width / stepover) + 1) num_passes = max(1, int(width / stepover) + 1)
path_points = []
y = cy_min
for i in range(num_passes):
y_pos = round(y + i * stepover, 2)
if y_pos > cy_max:
y_pos = round(cy_max, 2)
if i % 2 == 0:
path_points.append([round(cx_min, 2), y_pos, finish_z])
path_points.append([round(cx_max, 2), y_pos, finish_z])
else:
path_points.append([round(cx_max, 2), y_pos, finish_z])
path_points.append([round(cx_min, 2), y_pos, finish_z])
surface_roughness = self._estimate_surface_roughness( surface_roughness = self._estimate_surface_roughness(
tool["diameter"], stepover tool["diameter"], stepover
) )
toolpath_length = num_passes * length * 1.05 toolpath_length = num_passes * length * 1.05
machining_time = CuttingParamsCalculator.estimate_machining_time( machining_time = CuttingParamsCalculator.estimate_machining_time(
toolpath_length, cutting_params["feed_rate_mm_min"] toolpath_length, cutting_params["feed_rate_mm_min"]
) )
@@ -361,6 +455,7 @@ class FinishingToolpathGenerator:
"estimated_time_min": round(machining_time, 1), "estimated_time_min": round(machining_time, 1),
"cutting_direction": "one_way", "cutting_direction": "one_way",
"stepover_type": "scallop", "stepover_type": "scallop",
"path_points": path_points,
} }
def generate_contour_finishing(self, cavity_bbox: Dict, tool: Dict, def generate_contour_finishing(self, cavity_bbox: Dict, tool: Dict,
@@ -411,6 +506,62 @@ class FinishingToolpathGenerator:
h = stepover ** 2 / (8 * r) if r > 0 else stepover h = stepover ** 2 / (8 * r) if r > 0 else stepover
return h * 0.25 return h * 0.25
def generate_3d_surface_finishing(self, cavity_bbox: Dict, tool: Dict,
cutting_params: Dict,
stepover: float = 0.5,
angle: float = 0.0) -> Dict[str, Any]:
"""
Ballnose 三维曲面精加工 — Z 随形走刀。
在型腔边界框内,沿 Y 方向逐行走刀,Z 高度按余弦曲线随 Y 变化,
模拟曲面轮廓加工。适用于球头刀对型腔底面的随形精加工。
"""
dims = cavity_bbox.get("dimensions", [100, 100, 50])
cx_min, cy_min, cz_min = cavity_bbox.get("min", [0, 0, 0])
cx_max, cy_max, cz_max = cavity_bbox.get("max", [0, 0, 0])
width = dims[0]
length = dims[1]
depth = dims[2]
mid_y = (cy_min + cy_max) / 2.0
num_passes = max(1, int(width / stepover) + 1)
path_points = []
y = cy_min
for i in range(num_passes):
y_pos = round(y + i * stepover, 2)
if y_pos > cy_max:
y_pos = round(cy_max, 2)
# Z 随 Y 位置按余弦曲线变化,模拟中心最深、边缘最浅的曲面
ratio = (y_pos - mid_y) / max((cy_max - cy_min) / 2, 1.0)
z_depth = cz_max - depth * 0.85 * (1.0 - min(abs(ratio), 1.0))
z_finish = round(max(cz_min + 0.1, z_depth), 2)
if i % 2 == 0:
path_points.append([round(cx_min, 2), y_pos, z_finish])
path_points.append([round(cx_max, 2), y_pos, z_finish])
else:
path_points.append([round(cx_max, 2), y_pos, z_finish])
path_points.append([round(cx_min, 2), y_pos, z_finish])
surface_roughness = self._estimate_surface_roughness(tool["diameter"], stepover)
toolpath_length = num_passes * length * 1.1
machining_time = CuttingParamsCalculator.estimate_machining_time(
toolpath_length, cutting_params["feed_rate_mm_min"]
)
return {
"strategy": "3d_surface_finishing",
"tool": cutting_params,
"stepover": stepover,
"angle": angle,
"num_passes": num_passes,
"surface_roughness_ra": round(surface_roughness, 3),
"estimated_toolpath_length": round(toolpath_length, 1),
"estimated_time_min": round(machining_time, 1),
"note": "3D 曲面随形加工,Z 高度沿 Y 方向余弦变化",
"path_points": path_points,
}
class GCodePostProcessor: class GCodePostProcessor:
"""G代码后处理器""" """G代码后处理器"""
@@ -487,31 +638,66 @@ class GCodePostProcessor:
feed = tool_info.get("feed_rate_mm_min", 500) feed = tool_info.get("feed_rate_mm_min", 500)
levels = op.get("levels", []) levels = op.get("levels", [])
path_points = op.get("path_points", [])
if strategy == "z_level_roughing" and levels: if strategy == "z_level_roughing" and levels:
for level in levels: for level in levels:
z = level["z"] z = level["z"]
lines.append(f"(--- Z层 {level['level_index']}: Z={z:.2f} ---)") lines.append(f"(--- Z层 {level['level_index']}: Z={z:.2f} ---)")
lines.append(f"{d['linear']} Z{z:.2f} F{int(feed * 0.5)}") lines.append(f"{d['linear']} Z{z:.2f} F{int(feed * 0.5)}")
lines.append(f"{d['linear']} X50.0 Y30.0 F{feed}") # 筛选当前 Z 层的刀路点
lines.append(f"{d['linear']} X-50.0 Y30.0") layer_points = [p for p in path_points if abs(p[2] - z) < 0.01]
lines.append(f"{d['linear']} X-50.0 Y-30.0") if layer_points:
lines.append(f"{d['linear']} X50.0 Y-30.0") for pt in layer_points:
lines.append(f"{d['linear']} X{pt[0]:.2f} Y{pt[1]:.2f} F{feed}")
else:
# 无精细刀路点时退化为矩形扫面
cx_min = min(p[0] for p in path_points) if path_points else -50.0
cx_max = max(p[0] for p in path_points) if path_points else 50.0
cy_min = min(p[1] for p in path_points) if path_points else -30.0
cy_max = max(p[1] for p in path_points) if path_points else 30.0
lines.append(f"{d['linear']} X{cx_max:.2f} Y{cy_min:.2f} F{feed}")
lines.append(f"{d['linear']} X{cx_min:.2f} Y{cy_min:.2f}")
lines.append(f"{d['linear']} X{cx_min:.2f} Y{cy_max:.2f}")
lines.append(f"{d['linear']} X{cx_max:.2f} Y{cy_max:.2f}")
lines.append(f"{d['rapid']} Z10.0") lines.append(f"{d['rapid']} Z10.0")
lines.append("") lines.append("")
elif strategy in ("parallel_finishing", "contour_finishing"): elif strategy in ("parallel_finishing", "contour_finishing"):
num_passes = op.get("num_passes", 10) finish_z = path_points[0][2] if path_points else -5.0
stepover = op.get("stepover", 0.3) lines.append(f"{d['linear']} Z{finish_z:.2f} F{int(feed * 0.3)}")
if path_points:
for i in range(num_passes): for pt in path_points:
y = i * stepover - 30 lines.append(f"{d['linear']} X{pt[0]:.2f} Y{pt[1]:.2f} F{feed}")
lines.append(f"{d['linear']} Z-5.0 F{int(feed * 0.3)}") else:
lines.append(f"{d['linear']} X50.0 Y{y:.2f} F{feed}") num_passes = op.get("num_passes", 10)
lines.append(f"{d['linear']} X-50.0 Y{y:.2f}") stepover = op.get("stepover", 0.3)
for i in range(num_passes):
y = i * stepover - 30
lines.append(f"{d['linear']} X50.0 Y{y:.2f} F{feed}")
lines.append(f"{d['linear']} X-50.0 Y{y:.2f}")
lines.append(f"{d['rapid']} Z5.0") lines.append(f"{d['rapid']} Z5.0")
lines.append("") lines.append("")
elif strategy == "core_roughing" and levels:
for level in levels:
z = level["z"]
lines.append(f"(--- 型芯Z层 {level['level_index']}: Z={z:.2f} ---)")
lines.append(f"{d['linear']} Z{z:.2f} F{int(feed * 0.5)}")
layer_points = [p for p in path_points if abs(p[2] - z) < 0.01]
for pt in layer_points:
lines.append(f"{d['linear']} X{pt[0]:.2f} Y{pt[1]:.2f} F{feed}")
lines.append(f"{d['rapid']} Z10.0")
lines.append("")
elif strategy == "3d_surface_finishing":
lines.append(f"(--- 3D 曲面精加工,球头刀随形 ---)")
if path_points:
for pt in path_points:
lines.append(f"{d['linear']} X{pt[0]:.2f} Y{pt[1]:.2f} Z{pt[2]:.2f} F{feed}")
lines.append(f"{d['rapid']} Z10.0")
lines.append("")
else: else:
lines.append(f"(策略 {strategy} 的刀路数据)") lines.append(f"(策略 {strategy} 的刀路数据)")
lines.append("") lines.append("")
@@ -540,25 +726,50 @@ class MoldCAMDesigner:
self.roughing_gen = RoughingToolpathGenerator() self.roughing_gen = RoughingToolpathGenerator()
self.finishing_gen = FinishingToolpathGenerator() self.finishing_gen = FinishingToolpathGenerator()
self.post_processor = GCodePostProcessor() self.post_processor = GCodePostProcessor()
self._collision_detector = None
self._toolpath_optimizer = None
self._edm_designer = None
self._machining_simulator = None
def _get_collision_detector(self):
if self._collision_detector is None:
from moldinsight.core.mold_machining import CollisionDetector
self._collision_detector = CollisionDetector()
return self._collision_detector
def _get_toolpath_optimizer(self):
if self._toolpath_optimizer is None:
from moldinsight.core.mold_machining import ToolpathOptimizer
self._toolpath_optimizer = ToolpathOptimizer()
return self._toolpath_optimizer
def _get_edm_designer(self):
if self._edm_designer is None:
from moldinsight.core.mold_machining import EDMElectrodeDesigner
self._edm_designer = EDMElectrodeDesigner()
return self._edm_designer
def _get_machining_simulator(self):
if self._machining_simulator is None:
from moldinsight.core.mold_machining import MachiningSimulator
self._machining_simulator = MachiningSimulator()
return self._machining_simulator
def design_mold_cam(self, cavity_bbox: Dict, stock_bbox: Dict, def design_mold_cam(self, cavity_bbox: Dict, stock_bbox: Dict,
mold_steel: str = "P20", mold_steel: str = "P20",
surface_quality: str = "standard", surface_quality: str = "standard",
controller: str = "fanuc", controller: str = "fanuc",
program_number: int = 1000) -> Dict[str, Any]: program_number: int = 1000,
core_bbox: Optional[Dict] = None,
undercut_regions: Optional[List[Dict]] = None) -> Dict[str, Any]:
""" """
综合设计模具CAM方案 综合设计模具CAM方案(型腔 + 型芯 + 3D 曲面 + EDM + 仿真)。
Args: Args:
cavity_bbox: 型腔边界框 cavity_bbox: 型腔边界框
stock_bbox: 毛坯边界框 stock_bbox: 毛坯边界框
mold_steel: 模具钢材料 core_bbox: 型芯边界框(可选,提供则生成型芯刀路)
surface_quality: 表面质量要求 undercut_regions: 倒扣区域列表(可选,提供则生成 EDM 方案)
controller: 数控系统
program_number: 程序号
Returns:
完整的CAM方案
""" """
logger.info(f"开始模具CAM设计: 钢材={mold_steel}, 质量={surface_quality}") logger.info(f"开始模具CAM设计: 钢材={mold_steel}, 质量={surface_quality}")
@@ -584,6 +795,79 @@ class MoldCAMDesigner:
operations = [roughing_op, finishing_op] operations = [roughing_op, finishing_op]
# 型芯粗加工(提供 core_bbox 时)
core_op = None
if core_bbox:
core_op = self.roughing_gen.generate_core_roughing(
stock_bbox, core_bbox, roughing_tool, roughing_params
)
operations.append(core_op)
# 3D 曲面精加工(ballnose 随形)
surface_op = self.finishing_gen.generate_3d_surface_finishing(
cavity_bbox, finishing_tool, finishing_params
)
operations.append(surface_op)
# 刀路优化与碰撞检测
manufacturing_warnings = self._generate_cam_recommendations(
roughing_op, finishing_op, mold_steel
)
try:
optimizer = self._get_toolpath_optimizer()
for op in operations:
pts = op.get("path_points", [])
if pts and len(pts) >= 2:
optimized = optimizer.optimize_toolpath(
pts,
{"feed_rate_mm_min": op.get("tool", {}).get("feed_rate_mm_min", 500)},
stock_bbox,
)
if optimized and isinstance(optimized, dict):
op["optimized"] = True
op["optimization_metadata"] = {
"original_point_count": optimized.get("original_point_count"),
"corner_slowdowns": optimized.get("corner_slowdowns"),
"recommendations": optimized.get("recommendations"),
}
detector = self._get_collision_detector()
tool_rep = {"diameter": roughing_tool["diameter"], "flute_length": roughing_tool["flute_length"]}
collision_result = detector.check_toolpath_safety(
roughing_op.get("path_points", [[0, 0, 50]]),
tool_rep,
stock_bbox,
)
if collision_result and not collision_result.get("is_safe", True):
manufacturing_warnings.append(
f"碰撞检测发现 {collision_result.get('total_issues', 0)} 处潜在碰撞风险,请人工复核安全平面"
)
except Exception as exc:
logger.debug(f"刀路优化/碰撞检测跳过(非致命): {exc}")
# EDM 电极设计
edm_result = None
if undercut_regions:
try:
edm = self._get_edm_designer()
edm_result = edm.design_electrodes(
undercut_regions=undercut_regions,
cavity_bbox=cavity_bbox,
material="copper",
)
except Exception as exc:
logger.debug(f"EDM 电极设计跳过(非致命): {exc}")
# 加工仿真
simulation_result = None
try:
sim = self._get_machining_simulator()
simulation_result = sim.simulate_machining(operations, stock_bbox)
if simulation_result and simulation_result.get("gouging_detected"):
manufacturing_warnings.append("仿真检测到过切风险,请人工复核刀路")
except Exception as exc:
logger.debug(f"加工仿真跳过(非致命): {exc}")
gcode = self.post_processor.generate_gcode( gcode = self.post_processor.generate_gcode(
operations, program_number=program_number operations, program_number=program_number
) )
@@ -608,9 +892,9 @@ class MoldCAMDesigner:
"surface_quality": surface_quality, "surface_quality": surface_quality,
"controller": controller, "controller": controller,
}, },
"recommendations": self._generate_cam_recommendations( "recommendations": manufacturing_warnings,
roughing_op, finishing_op, mold_steel "edm": edm_result,
), "simulation": simulation_result,
} }
logger.info(f"CAM设计完成: {len(operations)} 个工序, " logger.info(f"CAM设计完成: {len(operations)} 个工序, "
@@ -29,6 +29,8 @@ class CAMBundleService:
mold_steel=mold_steel, mold_steel=mold_steel,
surface_quality=surface_quality, surface_quality=surface_quality,
controller=controller, controller=controller,
core_bbox=cavity_bbox, # MVP: 型芯 bbox 复用型腔 bbox
undercut_regions=scheme.get("undercut_regions") if scheme else None,
) )
process_plan = self._build_process_plan(cam_result.get("operations", [])) process_plan = self._build_process_plan(cam_result.get("operations", []))
@@ -55,6 +57,26 @@ class CAMBundleService:
if include_gcode: if include_gcode:
bundle["gcode"] = cam_result.get("gcode", "") bundle["gcode"] = cam_result.get("gcode", "")
bundle["gcode_lines"] = cam_result.get("gcode_lines", 0) bundle["gcode_lines"] = cam_result.get("gcode_lines", 0)
# 可选:EDM 电极方案
edm = cam_result.get("edm")
if edm and edm.get("electrodes"):
bundle["edm"] = {
"electrode_count": edm.get("total_electrode_count", 0),
"total_volume_cm3": edm.get("total_volume_cm3", 0),
"material": edm.get("material", "copper"),
"recommendations": edm.get("recommendations", []),
}
# 可选:加工仿真摘要
sim = cam_result.get("simulation")
if sim:
bundle["simulation"] = {
"removed_percent": sim.get("total_volume_removed_percent", 0),
"gouging_detected": bool(sim.get("gouging_detected")),
"quality": sim.get("quality_assessment", {}),
}
return bundle return bundle
@staticmethod @staticmethod
@@ -494,7 +494,7 @@ class ProcessingService:
"generated_at": datetime.now().isoformat(), "generated_at": datetime.now().isoformat(),
"schemes": {}, "schemes": {},
} }
components = ["cavity", "core", "parting_surface"] components = ["cavity", "core", "parting_surface", "product", "a_plate", "b_plate"]
for scheme_id, cavity_data in export_shapes.items(): for scheme_id, cavity_data in export_shapes.items():
try: try: