from datetime import datetime from fastapi import APIRouter, HTTPException, Query, Response from app.services import ( CameraCaptureError, CameraUnavailableError, MqttCaptureBusyError, MqttCaptureError, get_camera_service, get_mqtt_capture_service, ) router = APIRouter( prefix="/camera", tags=["camera"], ) @router.api_route("/capture", methods=["GET"], summary="拍照并直接返回图片") def capture_image( download: bool = Query( default=False, description="是否以附件下载方式返回图片。默认 False,浏览器会直接预览。", ), ) -> Response: """ 拍照接口。 调用这个接口时,服务会即时触发树莓派相机拍照, 然后把最新拍到的图片直接作为 HTTP 响应返回。 """ camera_service = get_camera_service() try: image_bytes = camera_service.capture_jpeg() except (CameraUnavailableError, CameraCaptureError) as exc: raise HTTPException(status_code=503, detail=str(exc)) from exc filename = f"capture_{datetime.now().strftime('%Y%m%d_%H%M%S')}.jpg" disposition = "attachment" if download else "inline" return Response( content=image_bytes, media_type="image/jpeg", headers={ "Content-Disposition": f'{disposition}; filename="{filename}"', "Cache-Control": "no-store", }, ) @router.api_route( "/capture-pair", methods=["GET", "POST"], summary="通过 MQTT 联动拍摄正反两张图片", ) def capture_pair_via_mqtt() -> dict[str, object]: """ 机械臂联动拍照接口。 调用流程: 1. 本接口收到请求后,临时连接 MQTT。 2. 发送 start 指令给机械臂。 3. 收到 `id=1` / `id=2` 的拍照指令后,分别覆盖保存到静态目录。 4. 第一张图保存后,调用识别接口获取卡牌信息。 5. 等待 `status=4` 作为流程结束信号。 6. 上传两张图到 MinIO,并返回 MinIO URL 和识别结果。 """ mqtt_capture_service = get_mqtt_capture_service() try: result = mqtt_capture_service.capture_pair() except MqttCaptureBusyError as exc: raise HTTPException(status_code=409, detail=str(exc)) from exc except MqttCaptureError as exc: raise HTTPException(status_code=503, detail=str(exc)) from exc return result