import asyncio from datetime import datetime from typing import Optional from fastapi import APIRouter, Depends, HTTPException from pydantic import BaseModel from sqlalchemy import select from sqlalchemy.ext.asyncio import AsyncSession from shared.database.database import get_db_session from shared.models.identity import User from moldinsight.models import ProcessingTask from shared.services.auth_service import get_current_active_user from moldinsight.services.cam_bundle_service import cam_bundle_service from moldinsight.services.task_query_service import TaskQueryService from shared.utils.logger import get_logger logger = get_logger(__name__) router = APIRouter() DEFAULT_CAM_PREFERENCES = { "mold_steel": "P20", "surface_quality": "standard", "controller": "fanuc", "include_gcode": True, } class CamPlanRequest(BaseModel): """未提供的偏好字段回落到任务持久化偏好,再回落到默认值。""" task_id: str scheme_id: Optional[str] = None mold_steel: Optional[str] = None surface_quality: Optional[str] = None controller: Optional[str] = None include_gcode: Optional[bool] = None @router.post("/cam/plan") async def generate_cam_plan( body: CamPlanRequest, db_session: AsyncSession = Depends(get_db_session), current_user: User = Depends(get_current_active_user), ): """基于任务分模结果生成 CAM 准备包(MVP)。""" _ = current_user task_id = body.task_id task_result = await db_session.execute( select(ProcessingTask).where(ProcessingTask.task_id == task_id) ) processing_task = task_result.scalar_one_or_none() persisted_preferences = {} if processing_task and isinstance(processing_task.parameters, dict): persisted_preferences = ( processing_task.parameters.get("cam_preferences", {}) or {} ) # 注意 include_gcode 显式判 None:False 是有效值,不能走 or 回落 mold_steel = ( body.mold_steel if body.mold_steel is not None else persisted_preferences.get("mold_steel", DEFAULT_CAM_PREFERENCES["mold_steel"]) ) surface_quality = ( body.surface_quality if body.surface_quality is not None else persisted_preferences.get("surface_quality", DEFAULT_CAM_PREFERENCES["surface_quality"]) ) controller = ( body.controller if body.controller is not None else persisted_preferences.get("controller", DEFAULT_CAM_PREFERENCES["controller"]) ) include_gcode = ( body.include_gcode if body.include_gcode is not None else persisted_preferences.get("include_gcode", DEFAULT_CAM_PREFERENCES["include_gcode"]) ) task_view = await TaskQueryService.get_task_view(db_session, task_id) if not task_view: raise HTTPException(status_code=404, detail="任务不存在") if task_view.get("status") != "completed": raise HTTPException(status_code=400, detail="任务尚未完成,无法生成CAM计划") try: # CAM 刀路计算为纯 Python 重计算,投放线程池避免阻塞事件循环 data = await asyncio.to_thread( cam_bundle_service.build_bundle, task_view=task_view, scheme_id=body.scheme_id, mold_steel=mold_steel, surface_quality=surface_quality, controller=controller, include_gcode=include_gcode, ) cam_preferences = { "mold_steel": mold_steel, "surface_quality": surface_quality, "controller": controller, "include_gcode": include_gcode, } if processing_task: parameters = processing_task.parameters if isinstance(processing_task.parameters, dict) else {} parameters["cam_preferences"] = cam_preferences parameters["cam_last_plan"] = { "scheme_id": data.get("scheme_id"), "generated_at": datetime.now().isoformat(), } processing_task.parameters = parameters await db_session.commit() # parameters 已变更,任务视图缓存失效 TaskQueryService.invalidate_task_view(task_id) return {"status": "success", "data": data, "cam_preferences": cam_preferences} except Exception as exc: logger.error(f"生成CAM准备包失败 task_id={task_id}: {exc}") raise HTTPException(status_code=500, detail=f"生成CAM准备包失败: {exc}")