This commit is contained in:
2026-05-02 00:39:04 +08:00
parent f9acdb767b
commit d1a3f22785
16 changed files with 1181 additions and 113 deletions
+3
View File
@@ -325,6 +325,9 @@ class CalculationService:
"title": "推荐方案",
"method": "legacy_fallback",
"score": 60.0,
"confidence_score": 45.0,
"is_fallback": True,
"fallback_reason": "多方案生成失败,已降级为兼容单方案输出",
"score_breakdown": {},
"summary": "当前模型未生成多方案,返回兼容单方案结果",
"parting": {
+187
View File
@@ -0,0 +1,187 @@
from typing import Dict, Any, List, Optional
from core.mold_cam import MoldCAMDesigner
class CAMBundleService:
"""将分模任务结果组装为 CAM 准备包(MVP 骨架)。"""
def __init__(self) -> None:
self.cam_designer = MoldCAMDesigner()
def build_bundle(
self,
task_view: Dict[str, Any],
scheme_id: Optional[str] = None,
mold_steel: str = "P20",
surface_quality: str = "standard",
controller: str = "fanuc",
include_gcode: bool = False,
) -> Dict[str, Any]:
scheme = self._select_scheme(task_view, scheme_id)
cavity_data = scheme.get("cavity_data", {}) if scheme else {}
cavity_bbox = self._extract_cavity_bbox(cavity_data)
stock_bbox = self._build_stock_bbox(cavity_data, cavity_bbox)
cam_result = self.cam_designer.design_mold_cam(
cavity_bbox=cavity_bbox,
stock_bbox=stock_bbox,
mold_steel=mold_steel,
surface_quality=surface_quality,
controller=controller,
)
process_plan = self._build_process_plan(cam_result.get("operations", []))
tooling_suggestion = self._build_tooling_suggestion(cam_result.get("tools", {}))
manufacturing_warnings = self._build_warnings(
scheme=scheme,
cam_recommendations=cam_result.get("recommendations", []),
cavity_data=cavity_data,
)
bundle = {
"task_id": task_view.get("task_id"),
"scheme_id": (scheme or {}).get("scheme_id"),
"process_plan": process_plan,
"tooling_suggestion": tooling_suggestion,
"manufacturing_warnings": manufacturing_warnings,
"summary": cam_result.get("summary", {}),
"confidence": {
"score": (scheme or {}).get("confidence_score"),
"is_fallback": bool((scheme or {}).get("is_fallback", False)),
"fallback_reason": (scheme or {}).get("fallback_reason", ""),
},
}
if include_gcode:
bundle["gcode"] = cam_result.get("gcode", "")
bundle["gcode_lines"] = cam_result.get("gcode_lines", 0)
return bundle
@staticmethod
def _select_scheme(task_view: Dict[str, Any], scheme_id: Optional[str]) -> Dict[str, Any]:
schemes = task_view.get("candidate_schemes") or []
if not schemes:
return {
"scheme_id": "legacy",
"confidence_score": None,
"is_fallback": True,
"fallback_reason": "无候选分模方案,使用默认加工包",
"cavity_data": task_view.get("cavity_data", {}),
}
if scheme_id:
for scheme in schemes:
if scheme.get("scheme_id") == scheme_id:
return scheme
best_scheme_id = task_view.get("best_scheme_id")
if best_scheme_id:
for scheme in schemes:
if scheme.get("scheme_id") == best_scheme_id:
return scheme
return schemes[0]
@staticmethod
def _extract_cavity_bbox(cavity_data: Dict[str, Any]) -> Dict[str, Any]:
bbox = cavity_data.get("product_analysis", {}).get("bounding_box", {}) or {}
dims = bbox.get("dimensions") or [100.0, 100.0, 50.0]
if len(dims) < 3:
dims = [100.0, 100.0, 50.0]
center = bbox.get("center") or [0.0, 0.0, 0.0]
half_x = float(dims[0]) / 2.0
half_y = float(dims[1]) / 2.0
half_z = float(dims[2]) / 2.0
return {
"dimensions": [float(dims[0]), float(dims[1]), float(dims[2])],
"min": [float(center[0]) - half_x, float(center[1]) - half_y, float(center[2]) - half_z],
"max": [float(center[0]) + half_x, float(center[1]) + half_y, float(center[2]) + half_z],
}
@staticmethod
def _build_stock_bbox(cavity_data: Dict[str, Any], cavity_bbox: Dict[str, Any]) -> Dict[str, Any]:
mold_size = cavity_data.get("manufacturing_info", {}).get("estimated_mold_size", {}) or {}
dims = mold_size.get("length"), mold_size.get("width"), mold_size.get("height")
if not all(v is not None for v in dims):
dims = cavity_bbox.get("dimensions", [100.0, 100.0, 50.0])
dims = [float(dims[0]) * 1.4, float(dims[1]) * 1.4, max(80.0, float(dims[2]) * 1.8)]
else:
dims = [float(dims[0]), float(dims[1]), float(dims[2])]
return {
"dimensions": dims,
"min": [-dims[0] / 2.0, -dims[1] / 2.0, -dims[2] / 2.0],
"max": [dims[0] / 2.0, dims[1] / 2.0, dims[2] / 2.0],
}
@staticmethod
def _build_process_plan(operations: List[Dict[str, Any]]) -> List[Dict[str, Any]]:
plan = []
for idx, op in enumerate(operations, start=1):
plan.append({
"seq": idx,
"operation": op.get("strategy", "unknown"),
"estimated_time_min": op.get("estimated_time_min", 0),
"tool_id": op.get("tool", {}).get("tool_id"),
"feed_rate_mm_min": op.get("tool", {}).get("feed_rate_mm_min"),
"spindle_speed_rpm": op.get("tool", {}).get("spindle_speed_rpm"),
})
return plan
@staticmethod
def _build_tooling_suggestion(tools: Dict[str, Any]) -> Dict[str, Any]:
roughing = tools.get("roughing", {})
finishing = tools.get("finishing", {})
return {
"roughing_tool": {
"tool_id": roughing.get("tool_id"),
"tool_type": roughing.get("tool_type"),
"diameter_mm": roughing.get("tool_diameter"),
},
"finishing_tool": {
"tool_id": finishing.get("tool_id"),
"tool_type": finishing.get("tool_type"),
"diameter_mm": finishing.get("tool_diameter"),
},
}
@staticmethod
def _build_warnings(
scheme: Dict[str, Any],
cam_recommendations: List[str],
cavity_data: Dict[str, Any],
) -> List[str]:
warnings: List[str] = []
for violation in scheme.get("dfm_violations", []) or []:
level = str(violation.get("level", "medium")).upper()
message = violation.get("message")
if message:
warnings.append(f"DFM[{level}]: {message}")
if scheme.get("is_fallback"):
reason = scheme.get("fallback_reason") or "分模结果使用回退路径"
warnings.append(f"分模回退: {reason}")
confidence_score = float(scheme.get("confidence_score") or 0.0)
if confidence_score and confidence_score < 60.0:
warnings.append(f"方案可信度偏低({confidence_score:.1f}),建议人工复核分型面与倒扣机构")
force_text = str(cavity_data.get("manufacturing_info", {}).get("estimated_clamping_force", ""))
if "吨" in force_text:
try:
force_val = float(force_text.replace("吨", "").strip())
if force_val > 1000:
warnings.append("预估锁模力较高,建议复核设备吨位与模板强度")
except ValueError:
pass
warnings.extend(cam_recommendations[:3])
if not warnings:
warnings.append("未发现明显制造风险,建议进入工艺评审")
return warnings
cam_bundle_service = CAMBundleService()
+2
View File
@@ -180,6 +180,8 @@ class ProcessingService:
pointcloud_data = {
"points": mesh_result.get("points", []),
"normals": mesh_result.get("normals", []),
"vertices": mesh_result.get("vertices", []),
"faces": mesh_result.get("faces", []),
"point_count": mesh_result.get("point_count", 0),
}
+85 -7
View File
@@ -23,6 +23,63 @@ logger = get_logger(__name__)
class StorageIntegrationService:
"""存储集成服务 - PostgreSQL + RustFS"""
@staticmethod
def _resolve_best_scheme_payload(cavity_json: Dict[str, Any]) -> Dict[str, Any]:
"""从新多方案/旧单方案结构中解析推荐方案和型腔详情。"""
if not isinstance(cavity_json, dict):
return {
"best_scheme_id": None,
"best_scheme": {},
"best_cavity_data": {},
"key_info": {},
}
candidate_schemes = cavity_json.get("candidate_schemes") or []
if not candidate_schemes:
key_info = cavity_json.get("mold_cavities", {}).get("cavity_key_info", {})
return {
"best_scheme_id": cavity_json.get("best_scheme_id"),
"best_scheme": {},
"best_cavity_data": cavity_json,
"key_info": key_info,
}
best_scheme_id = cavity_json.get("best_scheme_id")
best_scheme = candidate_schemes[0]
if best_scheme_id:
for scheme in candidate_schemes:
if scheme.get("scheme_id") == best_scheme_id:
best_scheme = scheme
break
best_cavity_data = best_scheme.get("cavity_data", {}) if isinstance(best_scheme, dict) else {}
key_info = best_scheme.get("key_info", {}) if isinstance(best_scheme, dict) else {}
if not key_info:
key_info = best_cavity_data.get("mold_cavities", {}).get("cavity_key_info", {})
return {
"best_scheme_id": best_scheme.get("scheme_id") or best_scheme_id,
"best_scheme": best_scheme,
"best_cavity_data": best_cavity_data,
"key_info": key_info,
}
@staticmethod
def _parse_first_number(value: Any) -> Optional[float]:
if value is None:
return None
try:
return float(value)
except (TypeError, ValueError):
pass
import re
matches = re.findall(r"\d+(?:\.\d+)?", str(value))
if not matches:
return None
try:
return float(matches[0])
except (TypeError, ValueError):
return None
async def save_stp_file(self, session: AsyncSession,
file_path: Path,
original_filename: str,
@@ -272,12 +329,26 @@ class StorageIntegrationService:
file_hash=file_hash
)
# 3. 提取关键信息
metadata = cavity_json.get('metadata', {})
product_analysis = cavity_json.get('product_analysis', {})
manufacturing_info = cavity_json.get('manufacturing_info', {})
# 3. 提取关键信息(兼容多方案与单方案结构)
payload = self._resolve_best_scheme_payload(cavity_json)
best_scheme_id = payload.get("best_scheme_id")
best_scheme = payload.get("best_scheme") or {}
best_cavity_data = payload.get("best_cavity_data") or {}
metadata = best_cavity_data.get('metadata', {})
product_analysis = best_cavity_data.get('product_analysis', {})
manufacturing_info = best_cavity_data.get('manufacturing_info', {})
mold_size = manufacturing_info.get('estimated_mold_size', {})
key_info = cavity_json.get('mold_cavities', {}).get('cavity_key_info', {})
key_info = payload.get("key_info") or {}
if not key_info:
key_info = best_cavity_data.get('mold_cavities', {}).get('cavity_key_info', {})
mold_material = (
metadata.get("selected_material")
or manufacturing_info.get("recommended_material")
or 'Aluminum Alloy 7075'
)
parting_line_length = self._parse_first_number(
manufacturing_info.get("parting_line_length")
)
# 4. 创建PostgreSQL记录
mold_cavity = MoldCavityData(
@@ -286,9 +357,10 @@ class StorageIntegrationService:
storage_bucket=upload_result['bucket'],
# 模具参数
mold_material=manufacturing_info.get('recommended_material', 'Aluminum Alloy 7075'),
mold_material=mold_material,
shrinkage_rate=metadata.get('shrinkage_rate', 0.005),
draft_angle=metadata.get('draft_angle', 2.0),
parting_line_length=parting_line_length,
# 提取的摘要字段
cavity_key_info=key_info,
@@ -306,7 +378,13 @@ class StorageIntegrationService:
# 质量评估
weld_line_risk=key_info.get('quality_considerations', {}).get('potential_weld_lines'),
sink_mark_risk=key_info.get('quality_considerations', {}).get('sink_mark_areas'),
warpage_risk=key_info.get('quality_considerations', {}).get('warpage_risk')
warpage_risk=key_info.get('quality_considerations', {}).get('warpage_risk'),
# 多方案可信化摘要
best_scheme_id=best_scheme_id,
confidence_score=best_scheme.get("confidence_score"),
is_fallback=best_scheme.get("is_fallback"),
fallback_reason=best_scheme.get("fallback_reason"),
)
session.add(mold_cavity)
+5
View File
@@ -83,6 +83,10 @@ class TaskQueryService:
html_file_url = cavity_view.get("html_file")
# 构造与内存任务兼容的任务视图
cam_preferences = {}
if isinstance(processing_task.parameters, dict):
cam_preferences = processing_task.parameters.get("cam_preferences", {}) or {}
task_view = {
"task_id": processing_task.task_id,
"status": processing_task.status,
@@ -100,6 +104,7 @@ class TaskQueryService:
"cavity_data": cavity_view.get("cavity_data"),
"candidate_schemes": cavity_view.get("candidate_schemes", []),
"best_scheme_id": cavity_view.get("best_scheme_id"),
"cam_preferences": cam_preferences,
"plan_result": cavity_json,
"mesh_summary": mesh_summary,
"html_file": html_file_url,