# services/calculation_service.py """模具工程参数计算服务 — 从 process_file_core 中抽取的纯计算逻辑""" from typing import Dict, Any, List, Optional from datetime import datetime from pathlib import Path class CalculationService: """将 process_file_core 中的工程计算逻辑抽取为独立服务,方便单测和复用""" # ─── 基础计算 ─── @staticmethod def calculate_product_weight(volume_mm3: float, density: float) -> float: """计算产品重量(克)""" volume_cm3 = volume_mm3 / 1000 return volume_cm3 * density @staticmethod def calculate_projected_area(bbox_dims: List[float]) -> float: """计算投影面积(cm²),取 X、Y 方向""" if len(bbox_dims) >= 2: return (bbox_dims[0] * bbox_dims[1]) / 100 return 0.0 @staticmethod def calculate_cavity_count(product_weight_g: float, projected_area_cm2: float) -> int: """ 计算最优型腔数量 基于产品重量和投影面积: - 小产品(< 50g)可以多型腔 - 大产品(> 1000g)通常单型腔 """ if product_weight_g < 50: cavity_count = 8 elif product_weight_g < 100: cavity_count = 4 elif product_weight_g < 300: cavity_count = 2 else: cavity_count = 1 # 根据投影面积调整 if projected_area_cm2 > 400: cavity_count = 1 elif projected_area_cm2 > 200 and cavity_count > 2: cavity_count = 2 return cavity_count @staticmethod def calculate_clamping_force( projected_area_cm2: float, cavity_count: int, runner_ratio: float = 0.20, injection_pressure: float = 700, ) -> int: """ 计算所需夹紧力(吨) runner_ratio: 流道系统占型腔投影面积比(0.15-0.25) injection_pressure: 注塑压力 kg/cm² """ total_projected_area = projected_area_cm2 * cavity_count * (1 + runner_ratio) clamping_force_ton = int(total_projected_area * injection_pressure / 1000) return max(50, min(clamping_force_ton, 3000)) @staticmethod def calculate_wall_thickness(volume_mm3: float, surface_area_mm2: float) -> Dict[str, float]: """计算壁厚范围""" if surface_area_mm2 > 0 and volume_mm3 > 0: avg = (volume_mm3 / surface_area_mm2) * 0.6 return { "avg_thickness_mm": avg, "wall_thickness_min": avg * 0.7, "wall_thickness_max": avg * 1.3, } return { "avg_thickness_mm": 2.5, "wall_thickness_min": 2.0, "wall_thickness_max": 3.0, } @staticmethod def calculate_complexity(avg_thickness_mm: float) -> float: """计算复杂度评分(0~1)""" return min((avg_thickness_mm / 5.0), 1.0) if avg_thickness_mm > 0 else 0.5 @staticmethod def calculate_mold_size( bbox_dims: List[float], cavity_count: int, cavity_spacing: float = 30, edge_margin: float = 50, ) -> Dict[str, float]: """计算模具尺寸(长×宽×高),单位 mm""" dim_x = max(bbox_dims[0] if len(bbox_dims) > 0 else 120, 120) dim_y = max(bbox_dims[1] if len(bbox_dims) > 1 else 100, 100) dim_z = max(bbox_dims[2] if len(bbox_dims) > 2 else 60, 60) if cavity_count == 1: length = dim_x + 2 * edge_margin width = dim_y + 2 * edge_margin elif cavity_count == 2: length = 2 * dim_x + cavity_spacing + 2 * edge_margin width = dim_y + 2 * edge_margin elif cavity_count == 4: length = 2 * dim_x + cavity_spacing + 2 * edge_margin width = 2 * dim_y + cavity_spacing + 2 * edge_margin else: # 8 型腔: 2x4 length = 4 * dim_x + 3 * cavity_spacing + 2 * edge_margin width = 2 * dim_y + cavity_spacing + 2 * edge_margin height = dim_z + 80 # 包含冷却系统 return {"length": length, "width": width, "height": height} @staticmethod def calculate_parting_line_length(bbox_dims: List[float], cavity_count: int) -> float: """计算分型线长度(mm)""" if len(bbox_dims) >= 2: return 2 * (bbox_dims[0] + bbox_dims[1]) * cavity_count return 0.0 @staticmethod def calculate_cycle_time( wall_thickness_max: float, volume_cm3: float, cavity_count: int, ) -> int: """ 估算成型周期(秒) 周期 = 冷却时间 + 注塑时间 + 顶出时间 + 开合模时间 """ cooling_time = (wall_thickness_max ** 2) * 4 injection_time = max(3, volume_cm3 / 100) ejection_time = 3 cycle_time = cooling_time + injection_time + ejection_time + 5 # 多型腔需要更长冷却时间 if cavity_count > 1: cycle_time = cycle_time * (1 + 0.1 * (cavity_count - 1)) return int(cycle_time) # ─── 组装方法 ─── @classmethod def build_detailed_cavity_json( cls, geometry_data: Dict[str, Any], material: Dict[str, Any], file_path: str, cavity_mesh_data: Optional[Dict[str, Any]] = None, ) -> Dict[str, Any]: """ 组装完整的 detailed_cavity_json(整合以上所有计算结果) Args: geometry_data: STP 解析得到的几何数据 material: MaterialService.get_material() 返回的材料属性字典 file_path: STP 文件路径 cavity_mesh_data: 型腔网格数据(可选) """ volume_mm3 = geometry_data.get("volume", 0) surface_area_mm2 = geometry_data.get("surface_area", 0) bbox = geometry_data.get("bounding_box", {}) bbox_dims = bbox.get("dimensions", [0, 0, 0]) material_density = material["density"] shrinkage_rate = material["shrinkage"] # 各项计算 volume_cm3 = volume_mm3 / 1000 product_weight_g = cls.calculate_product_weight(volume_mm3, material_density) projected_area_cm2 = cls.calculate_projected_area(bbox_dims) cavity_count = cls.calculate_cavity_count(product_weight_g, projected_area_cm2) clamping_force_ton = cls.calculate_clamping_force(projected_area_cm2, cavity_count) wall = cls.calculate_wall_thickness(volume_mm3, surface_area_mm2) complexity_score = cls.calculate_complexity(wall["avg_thickness_mm"]) mold_size = cls.calculate_mold_size(bbox_dims, cavity_count) parting_line_length = cls.calculate_parting_line_length(bbox_dims, cavity_count) cycle_time = cls.calculate_cycle_time(wall["wall_thickness_max"], volume_cm3, cavity_count) injection_pressure = 700 # kg/cm² detailed_cavity_json = { "metadata": { "file_name": Path(file_path).name, "analysis_date": datetime.now().isoformat(), "shrinkage_rate": shrinkage_rate, "draft_angle": 2.0, "selected_material": material["name"], }, "product_analysis": { "volume": volume_mm3, "surface_area": surface_area_mm2, "bounding_box": bbox, }, "manufacturing_info": { "recommended_material": material["name"], "material_density": f"{material_density} g/cm³", "estimated_clamping_force": f"{clamping_force_ton} 吨", "estimated_mold_size": { "length": int(mold_size["length"]), "width": int(mold_size["width"]), "height": int(mold_size["height"]), }, "mold_material": "铝合金7075" if clamping_force_ton < 200 else "P20钢材", "mold_hardness": "HB 150-170" if clamping_force_ton < 200 else "HRC 28-32", "surface_finish": "Ra 0.8 μm", "parting_line_length": f"{parting_line_length:.2f} mm", "estimated_cycle_time": f"{cycle_time} 秒", "injection_pressure": f"{injection_pressure} kg/cm²", }, "mold_cavities": { "cavity_count": cavity_count, }, } # 合并型腔网格数据 if cavity_mesh_data and "mold_cavities" in cavity_mesh_data: mold_cavities = cavity_mesh_data["mold_cavities"] for key in ("cavity", "core", "parting_surface"): if key in mold_cavities: detailed_cavity_json["mold_cavities"][key] = mold_cavities[key] # 添加型腔关键信息 detailed_cavity_json["mold_cavities"]["cavity_key_info"] = { "geometric_characteristics": { "product_weight": f"{product_weight_g:.2f} g", "wall_thickness_range": f"{wall['wall_thickness_min']:.2f} - {wall['wall_thickness_max']:.2f} mm", "complexity_score": round(complexity_score, 2), "product_volume": f"{volume_cm3:.2f} cm³", "projected_area": f"{projected_area_cm2:.2f} cm²", }, "quality_considerations": { "potential_weld_lines": "center" if cavity_count > 1 else "minimal", "sink_mark_areas": "thick_sections" if wall["wall_thickness_max"] > 4 else "minimal", "warpage_risk": "medium" if wall["wall_thickness_max"] > 5 else "low", }, } return detailed_cavity_json