x
This commit is contained in:
@@ -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": {
|
||||
|
||||
@@ -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()
|
||||
@@ -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),
|
||||
}
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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,
|
||||
|
||||
Reference in New Issue
Block a user