分模候选方案

This commit is contained in:
2026-04-23 23:37:39 +08:00
parent 0bdfc7f7d5
commit c1f6b6e827
16 changed files with 1302 additions and 497 deletions
+75
View File
@@ -299,3 +299,78 @@ class CalculationService:
}
return detailed_cavity_json
@classmethod
def build_plan_result(
cls,
geometry_data: Dict[str, Any],
material: Dict[str, Any],
file_path: str,
plan_result: Optional[Dict[str, Any]] = None,
) -> Dict[str, Any]:
"""构建多方案分模结果,并保留单方案兼容字段。"""
if not plan_result or not plan_result.get("candidate_schemes"):
legacy = cls.build_detailed_cavity_json(
geometry_data=geometry_data,
material=material,
file_path=file_path,
cavity_mesh_data=None,
)
return {
"best_scheme_id": "scheme_1",
"candidate_schemes": [
{
"scheme_id": "scheme_1",
"rank": 1,
"title": "推荐方案",
"method": "legacy_fallback",
"score": 60.0,
"score_breakdown": {},
"summary": "当前模型未生成多方案,返回兼容单方案结果",
"parting": {
"axis": legacy.get("metadata", {}).get("parting_direction", "Z"),
"direction": None,
"surface": legacy.get("parting_surface", {}),
"line": [],
},
"cavity_data": legacy,
"key_info": legacy.get("mold_cavities", {}).get("cavity_key_info", {}),
"undercut_regions": legacy.get("undercut_regions", []),
"side_actions": legacy.get("side_actions", {}),
}
],
"global_summary": {
"scheme_count": 1,
"recommended_reason": "兼容旧版单方案结果",
},
"cavity_data": legacy,
"key_info": legacy.get("mold_cavities", {}).get("cavity_key_info", {}),
}
candidate_schemes = plan_result.get("candidate_schemes", [])
best_scheme = cls.get_best_scheme(plan_result)
result = {
"best_scheme_id": plan_result.get("best_scheme_id"),
"candidate_schemes": candidate_schemes,
"global_summary": plan_result.get("global_summary", {}),
"cavity_data": best_scheme.get("cavity_data", {}) if best_scheme else {},
"key_info": best_scheme.get("key_info", {}) if best_scheme else {},
}
return result
@staticmethod
def get_best_scheme(plan_result: Optional[Dict[str, Any]]) -> Optional[Dict[str, Any]]:
if not plan_result:
return None
schemes = plan_result.get("candidate_schemes", [])
if not schemes:
return None
best_scheme_id = plan_result.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]
+84 -34
View File
@@ -15,6 +15,7 @@ from core.mold_generator import MoldCavityGenerator
from core.aluminum_foam_mold import AluminumFoamMoldGenerator
from core.mold_quality_inspector import AluminumFoamMoldQualityInspector
from core.mesh_generator import MeshGenerator
from core.multi_scheme_planner import MultiSchemeMoldPlanner
from services.storage_integration_rustfs import StorageIntegrationService
from services.redis_task_manager import redis_task_manager
from services.material_service import MaterialService
@@ -39,6 +40,7 @@ class ProcessingService:
self.mesh_generator = MeshGenerator(quality="medium")
self.html_generator = HTMLGenerator()
self.storage_service = StorageIntegrationService()
self.multi_scheme_planner = MultiSchemeMoldPlanner()
# ─── 对外入口 ───
@@ -127,7 +129,7 @@ class ProcessingService:
selected_material = MaterialService.get_material(requested_material)
is_foam_material = MaterialService.is_foam_material(requested_material)
cavity_mesh_data = await self._step_generate_cavity(
plan_result = await self._step_generate_cavity(
shape, selected_material, is_foam_material
)
@@ -136,19 +138,25 @@ class ProcessingService:
db_session, task_id, "processing", 60, "生成型腔详细数据"
)
detailed_cavity_json = CalculationService.build_detailed_cavity_json(
detailed_cavity_json = CalculationService.build_plan_result(
geometry_data=geometry_data,
material=selected_material,
file_path=str(file_path),
cavity_mesh_data=cavity_mesh_data,
plan_result=plan_result,
)
if cavity_mesh_data and "mold_cavities" in cavity_mesh_data:
mold_cavities = cavity_mesh_data["mold_cavities"]
logger.info(f"型腔网格数据已合并: cavity {mold_cavities.get('cavity', {}).get('vertex_count', 0)} 顶点")
best_scheme = CalculationService.get_best_scheme(detailed_cavity_json)
best_cavity_data = best_scheme.get("cavity_data", {}) if best_scheme else {}
best_key_info = best_scheme.get("key_info", {}) if best_scheme else {}
if best_cavity_data.get("mold_cavities"):
cavity_geometry = best_cavity_data["mold_cavities"].get("cavity", {})
logger.info(
f"推荐方案型腔数据已合并: cavity {cavity_geometry.get('vertex_count', 0)} 顶点"
)
# 5. 生成关键信息
cavity_key_info = detailed_cavity_json["mold_cavities"]["cavity_key_info"]
cavity_key_info = best_key_info
# 6. 保存几何数据到数据库
await self.storage_service.update_task_status(
@@ -162,12 +170,7 @@ class ProcessingService:
geometry_data.get("analysis_method", "mold_cavity"),
)
# 7. 保存模具型腔数据
await self.storage_service.save_mold_cavity_data(
db_session, stp_file_id, detailed_cavity_json
)
# 8. 生成HTML可视化
# 7. 生成HTML可视化
await self.storage_service.update_task_status(
db_session, task_id, "processing", 85, "生成可视化报告"
)
@@ -180,10 +183,26 @@ class ProcessingService:
"point_count": mesh_result.get("point_count", 0),
}
detailed_cavity_json = await self._attach_scheme_previews(
detailed_cavity_json=detailed_cavity_json,
geometry_data=geometry_data,
stp_filename=Path(file_path).name,
pointcloud_data=pointcloud_data,
)
best_scheme = CalculationService.get_best_scheme(detailed_cavity_json)
best_cavity_data = best_scheme.get("cavity_data", {}) if best_scheme else best_cavity_data
best_key_info = best_scheme.get("key_info", {}) if best_scheme else best_key_info
# 8. 保存模具型腔数据(包含方案级预览链接)
await self.storage_service.save_mold_cavity_data(
db_session, stp_file_id, detailed_cavity_json
)
html_file_path = self.html_generator.generate_and_save_visualization(
geometry_data,
Path(file_path).name,
cavity_data=detailed_cavity_json,
cavity_data=best_cavity_data,
pointcloud_data=pointcloud_data,
)
@@ -233,9 +252,12 @@ class ProcessingService:
await redis_task_manager.update_task(task_id, {
"geometry_data": geometry_data,
"analysis_result": analysis_result,
"cavity_data": detailed_cavity_json,
"key_info": detailed_cavity_json,
"html_file": f"/html/{Path(html_file_path).name}",
"plan_result": detailed_cavity_json,
"candidate_schemes": detailed_cavity_json.get("candidate_schemes", []),
"best_scheme_id": detailed_cavity_json.get("best_scheme_id"),
"cavity_data": best_cavity_data,
"key_info": best_key_info,
"html_file": best_scheme.get("html_file", f"/html/{Path(html_file_path).name}") if best_scheme else f"/html/{Path(html_file_path).name}",
"verification": verification_result,
"status": ProcessingStatus.COMPLETED,
"completed_at": str(datetime.now()),
@@ -327,28 +349,24 @@ class ProcessingService:
async def _step_generate_cavity(
self, shape, selected_material: dict, is_foam_material: bool,
) -> Optional[Dict[str, Any]]:
"""生成模具型腔数据"""
cavity_mesh_data = None
"""生成多方案分模结果"""
plan_result = None
try:
if shape:
if is_foam_material:
self.aluminum_foam_generator.set_material(selected_material["name"])
cavity_result = self.aluminum_foam_generator.generate_mold_cavities(shape)
cavity_mesh_data = self.aluminum_foam_generator.generate_detailed_cavity_json(cavity_result)
logger.info(f"使用铝泡沫模具生成器: {selected_material['name']}")
else:
cavity_result = self.mold_generator.generate_mold_cavities(shape)
cavity_mesh_data = self.mold_generator.generate_detailed_cavity_json(cavity_result)
logger.info(f"使用普通塑料模具生成器: {selected_material['name']}")
if cavity_mesh_data:
logger.info(f"型腔网格数据生成完成: {cavity_mesh_data.get('mold_cavities', {}).get('cavity', {}).get('vertex_count', 0)} 顶点")
plan_result = self.multi_scheme_planner.generate_plan(
shape=shape,
material=selected_material,
is_foam_material=is_foam_material,
)
logger.info(
f"多方案分模完成: 生成 {len(plan_result.get('candidate_schemes', []))} 套方案"
)
except Exception as cavity_err:
logger.warning(f"型腔生成失败,使用简化数据: {cavity_err}")
logger.warning(f"多方案分模失败,使用简化数据: {cavity_err}")
traceback.print_exc()
cavity_mesh_data = None
plan_result = None
return cavity_mesh_data
return plan_result
async def _step_verify(
self, file_path: str, db_session: AsyncSession,
@@ -379,6 +397,38 @@ class ProcessingService:
logger.warning(f"FreeCAD验证失败(不影响主流程): {ve}")
return {"status": "error", "error": str(ve)}
async def _attach_scheme_previews(
self,
detailed_cavity_json: Dict[str, Any],
geometry_data: Dict[str, Any],
stp_filename: str,
pointcloud_data: Optional[Dict[str, Any]] = None,
) -> Dict[str, Any]:
"""为每个候选分模方案生成独立HTML预览链接。"""
candidate_schemes = detailed_cavity_json.get("candidate_schemes", [])
if not candidate_schemes:
return detailed_cavity_json
for scheme in candidate_schemes:
cavity_data = scheme.get("cavity_data")
if not cavity_data:
continue
suffix = scheme.get("scheme_id")
html_path = self.html_generator.generate_and_save_visualization(
geometry_data,
stp_filename,
cavity_data=cavity_data,
pointcloud_data=pointcloud_data,
suffix=suffix,
)
scheme["html_file"] = f"/html/{Path(html_path).name}"
best_scheme = CalculationService.get_best_scheme(detailed_cavity_json)
if best_scheme:
detailed_cavity_json["html_file"] = best_scheme.get("html_file")
return detailed_cavity_json
# ─── 指标持久化 ───
async def _save_analysis_metrics(self, session: AsyncSession, stp_file_id: int, analysis_result: dict):
+44 -2
View File
@@ -62,6 +62,7 @@ class TaskQueryService:
cavity_json: Optional[Dict[str, Any]] = file_with_data.get("mold_cavity_data")
features_json: List[Dict[str, Any]] = file_with_data.get("features", [])
recommendations_json: List[Dict[str, Any]] = file_with_data.get("recommendations", [])
cavity_view = TaskQueryService._extract_cavity_view(cavity_json)
# 组装网格摘要
mesh_summary = await TaskQueryService._get_mesh_summary(db_session, stp_file.id)
@@ -78,6 +79,8 @@ class TaskQueryService:
pass
if html_file_record and html_file_record.filename:
html_file_url = f"/html/{html_file_record.filename}"
if cavity_view.get("html_file"):
html_file_url = cavity_view.get("html_file")
# 构造与内存任务兼容的任务视图
task_view = {
@@ -93,8 +96,11 @@ class TaskQueryService:
if processing_task.completed_time
else "",
"geometry_data": geometry_json,
"key_info": cavity_json,
"cavity_data": cavity_json,
"key_info": cavity_view.get("key_info"),
"cavity_data": cavity_view.get("cavity_data"),
"candidate_schemes": cavity_view.get("candidate_schemes", []),
"best_scheme_id": cavity_view.get("best_scheme_id"),
"plan_result": cavity_json,
"mesh_summary": mesh_summary,
"html_file": html_file_url,
"analysis_result": {
@@ -141,3 +147,39 @@ class TaskQueryService:
"quality": mesh_record.quality,
}
return None
@staticmethod
def _extract_cavity_view(cavity_json: Optional[Dict[str, Any]]) -> Dict[str, Any]:
"""兼容旧单方案与新多方案结果视图。"""
if not cavity_json:
return {
"cavity_data": None,
"key_info": None,
"candidate_schemes": [],
"best_scheme_id": None,
}
candidate_schemes = cavity_json.get("candidate_schemes")
if candidate_schemes:
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
return {
"cavity_data": best_scheme.get("cavity_data"),
"key_info": best_scheme.get("key_info"),
"candidate_schemes": candidate_schemes,
"best_scheme_id": best_scheme_id or best_scheme.get("scheme_id"),
"html_file": best_scheme.get("html_file"),
}
return {
"cavity_data": cavity_json,
"key_info": cavity_json,
"candidate_schemes": [],
"best_scheme_id": None,
"html_file": cavity_json.get("html_file"),
}