cli to control zed camera start and stop. 2. measure now use every svo2 file for 1 fish, give intermideate result and final result with confidecne level(*).

This commit is contained in:
kevin
2026-04-16 11:38:30 +08:00
parent 9dce487c79
commit cc6cef0f73
57 changed files with 1877 additions and 386 deletions

125
fish_api/app/routers/zed.py Normal file
View File

@@ -0,0 +1,125 @@
"""ZED 分段录制 HTTP 控制(响应格式对齐 biomasscode/msg/data不校验 ingest API Key"""
from __future__ import annotations
from typing import Any, Dict, Optional
from fastapi import APIRouter, Body, Depends
from pydantic import BaseModel, Field
from starlette.responses import JSONResponse
from app.services.zed_recording_control import (
start_zed_recording,
stop_zed_recording,
zed_recording_is_running,
)
from app.settings import Settings, get_settings
from app.state import app_state
router = APIRouter(prefix="/api/v1/zed/recording", tags=["zed-recording"])
def _ok(data: Dict[str, Any], msg: str = "成功") -> JSONResponse:
return JSONResponse(
content={
"code": 200,
"msg": msg,
"data": data,
}
)
def _err(
http_status: int,
*,
code: int,
msg: str,
data: Optional[Dict[str, Any]] = None,
) -> JSONResponse:
body: Dict[str, Any] = {"code": code, "msg": msg}
if data is not None:
body["data"] = data
return JSONResponse(status_code=http_status, content=body)
class ZedRecordingStartRequest(BaseModel):
"""可选覆盖分段时长;``fish_id`` 由服务端每次启动在库中递增分配。"""
segment_sec: Optional[float] = Field(
None,
ge=1.0,
description="每段时长(秒);缺省使用 ZED_SVO_SEGMENT_SEC",
)
@router.get("/status")
async def zed_recording_status() -> JSONResponse:
data: Dict[str, Any] = {"running": zed_recording_is_running()}
if app_state.zed_recording_fish_id is not None:
data["fish_id"] = app_state.zed_recording_fish_id
return _ok(data)
@router.post("/start")
async def zed_recording_start(
body: ZedRecordingStartRequest = Body(default_factory=ZedRecordingStartRequest),
settings: Settings = Depends(get_settings),
) -> JSONResponse:
ok, msg, fish_id, _ = start_zed_recording(
settings,
segment_sec=body.segment_sec,
)
if not ok:
if msg == "already_running":
return _err(
409,
code=409,
msg="已在录制中",
data={"ok": False, "message": "已在录制中"},
)
if msg.startswith("session_db_error:"):
return _err(
500,
code=500,
msg="写入录制会话失败",
data={"ok": False, "message": msg},
)
return _err(
500,
code=500,
msg="启动录制失败",
data={"ok": False, "message": msg},
)
return _ok(
{
"ok": True,
"message": "录制开始",
"fish_id": fish_id,
}
)
@router.post("/stop")
async def zed_recording_stop(settings: Settings = Depends(get_settings)) -> JSONResponse:
ok, msg, fish_id = stop_zed_recording(settings)
if not ok:
if msg == "not_running":
return _ok({"ok": False, "message": "未在录制"}, msg="未在录制")
if msg == "stop_timeout":
return _err(
504,
code=504,
msg="停止超时",
data={"ok": False, "message": "停止超时"},
)
return _err(
500,
code=500,
msg="停止录制失败",
data={"ok": False, "message": msg},
)
out: Dict[str, Any] = {"ok": True, "message": "录制停止"}
if fish_id is not None:
out["fish_id"] = fish_id
return _ok(out)