2026-09-17 16:15:49 +08:00
|
|
|
|
import asyncio
|
|
|
|
|
|
from datetime import datetime
|
|
|
|
|
|
from typing import Optional
|
|
|
|
|
|
|
|
|
|
|
|
from fastapi import APIRouter, Depends, HTTPException
|
|
|
|
|
|
from pydantic import BaseModel
|
2026-05-02 00:39:04 +08:00
|
|
|
|
from sqlalchemy import select
|
|
|
|
|
|
from sqlalchemy.ext.asyncio import AsyncSession
|
|
|
|
|
|
|
2026-05-29 18:10:08 +08:00
|
|
|
|
from shared.database.database import get_db_session
|
2026-09-17 16:15:49 +08:00
|
|
|
|
from shared.models.identity import User
|
|
|
|
|
|
from moldinsight.models import ProcessingTask
|
2026-05-29 18:10:08 +08:00
|
|
|
|
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
|
2026-05-02 00:39:04 +08:00
|
|
|
|
|
|
|
|
|
|
logger = get_logger(__name__)
|
|
|
|
|
|
|
|
|
|
|
|
router = APIRouter()
|
|
|
|
|
|
DEFAULT_CAM_PREFERENCES = {
|
|
|
|
|
|
"mold_steel": "P20",
|
|
|
|
|
|
"surface_quality": "standard",
|
|
|
|
|
|
"controller": "fanuc",
|
2026-06-16 11:15:03 +08:00
|
|
|
|
"include_gcode": True,
|
2026-05-02 00:39:04 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
2026-09-17 16:15:49 +08:00
|
|
|
|
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
|
|
|
|
|
|
|
|
|
|
|
|
|
2026-05-02 00:39:04 +08:00
|
|
|
|
@router.post("/cam/plan")
|
|
|
|
|
|
async def generate_cam_plan(
|
2026-09-17 16:15:49 +08:00
|
|
|
|
body: CamPlanRequest,
|
2026-05-02 00:39:04 +08:00
|
|
|
|
db_session: AsyncSession = Depends(get_db_session),
|
|
|
|
|
|
current_user: User = Depends(get_current_active_user),
|
|
|
|
|
|
):
|
|
|
|
|
|
"""基于任务分模结果生成 CAM 准备包(MVP)。"""
|
|
|
|
|
|
_ = current_user
|
2026-09-17 16:15:49 +08:00
|
|
|
|
task_id = body.task_id
|
2026-05-02 00:39:04 +08:00
|
|
|
|
|
|
|
|
|
|
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 {}
|
|
|
|
|
|
)
|
|
|
|
|
|
|
2026-09-17 16:15:49 +08:00
|
|
|
|
# 注意 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"])
|
2026-05-02 00:39:04 +08:00
|
|
|
|
)
|
2026-09-17 16:15:49 +08:00
|
|
|
|
surface_quality = (
|
|
|
|
|
|
body.surface_quality
|
|
|
|
|
|
if body.surface_quality is not None
|
|
|
|
|
|
else persisted_preferences.get("surface_quality", DEFAULT_CAM_PREFERENCES["surface_quality"])
|
2026-05-02 00:39:04 +08:00
|
|
|
|
)
|
2026-09-17 16:15:49 +08:00
|
|
|
|
controller = (
|
|
|
|
|
|
body.controller
|
|
|
|
|
|
if body.controller is not None
|
|
|
|
|
|
else persisted_preferences.get("controller", DEFAULT_CAM_PREFERENCES["controller"])
|
2026-05-02 00:39:04 +08:00
|
|
|
|
)
|
2026-09-17 16:15:49 +08:00
|
|
|
|
include_gcode = (
|
|
|
|
|
|
body.include_gcode
|
|
|
|
|
|
if body.include_gcode is not None
|
|
|
|
|
|
else persisted_preferences.get("include_gcode", DEFAULT_CAM_PREFERENCES["include_gcode"])
|
2026-05-02 00:39:04 +08:00
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
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:
|
2026-09-17 16:15:49 +08:00
|
|
|
|
# CAM 刀路计算为纯 Python 重计算,投放线程池避免阻塞事件循环
|
|
|
|
|
|
data = await asyncio.to_thread(
|
|
|
|
|
|
cam_bundle_service.build_bundle,
|
2026-05-02 00:39:04 +08:00
|
|
|
|
task_view=task_view,
|
2026-09-17 16:15:49 +08:00
|
|
|
|
scheme_id=body.scheme_id,
|
2026-05-02 00:39:04 +08:00
|
|
|
|
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()
|
2026-08-31 18:01:34 +08:00
|
|
|
|
# parameters 已变更,任务视图缓存失效
|
|
|
|
|
|
TaskQueryService.invalidate_task_view(task_id)
|
2026-05-02 00:39:04 +08:00
|
|
|
|
|
|
|
|
|
|
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}")
|