Files
FishServer/scripts/biomass_poller.py
zaiun xu 5e1b2117c1 feat(fish_api): SQLite 快照投递、日志与 watch 空闲告警
- 新增 SQLite:measure/health 快照、delivery_cursor 单消费者 pop;clear/start_fresh 可清空库
- biomass GET 仅返回约定 data 字段,X-Fish-Biomass-New 表示是否有新快照;poller 读响应头
- loguru 桥接 uvicorn,子进程 stdout 流式输出;format_json_pretty 与算法摘要日志
- measure/action watch 无新任务时限流 WARNING;watch_idle 共用逻辑
- 依赖 loguru;新增 db、logging_config、subprocess_run、watch_idle、启动脚本

FishMeasure: 更新 fish_video_weight_evaluation 与 predict_weigth_from_svo2;移除未用 refbox/segmentation 脚本
Made-with: Cursor
2026-04-09 11:54:30 +08:00

100 lines
3.1 KiB
Python
Executable File
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/usr/bin/env python3
"""
独立进程:轮询 Fish API 的两个结果接口,用 loguru 输出响应。
接口每次 GET 会「消费」一条未投递快照;仅当响应头 X-Fish-Biomass-New: 1或 JSON code!=200时打日志。
BIOMASS_API_BASE=http://127.0.0.1:8000 POLL_INTERVAL=5 \\
python scripts/biomass_poller.py
依赖(与 fish_api dev 组一致httpx、loguru
cd fish_api && pip install -e ".[dev]"
"""
from __future__ import annotations
import argparse
import asyncio
import json
import os
import sys
from typing import Any
import httpx
from loguru import logger
def _fmt_body(resp: httpx.Response) -> Any:
try:
return resp.json()
except Exception:
return resp.text
def _should_log(resp: httpx.Response, body: Any) -> bool:
"""有新投递的快照X-Fish-Biomass-New: 1或业务错误JSON code!=200时打日志。"""
if not isinstance(body, dict):
return bool(body)
if body.get("code") != 200:
return True
return resp.headers.get("X-Fish-Biomass-New", "").strip() == "1"
async def poll_once(client: httpx.AsyncClient, base: str) -> None:
base = base.rstrip("/")
camera_url = f"{base}/api/v1/biomass/real/camera/"
health_url = f"{base}/api/v1/biomass/health/result/"
r1 = await client.get(camera_url)
r2 = await client.get(health_url)
b1 = _fmt_body(r1)
b2 = _fmt_body(r2)
if _should_log(r1, b1):
logger.info("[real/camera/] HTTP {} | {}", r1.status_code, json.dumps(b1, ensure_ascii=False))
if _should_log(r2, b2):
logger.info("[health/result/] HTTP {} | {}", r2.status_code, json.dumps(b2, ensure_ascii=False))
async def poll_loop(base: str, interval: float) -> None:
async with httpx.AsyncClient(timeout=30.0) as client:
while True:
try:
await poll_once(client, base)
except Exception:
logger.exception("poll round failed")
await asyncio.sleep(max(interval, 0.5))
def main() -> None:
parser = argparse.ArgumentParser(description="Poll Fish API biomass GET endpoints.")
parser.add_argument(
"--base-url",
default=os.environ.get("BIOMASS_API_BASE", "http://127.0.0.1:8000"),
help="Fish API 根 URL默认 BIOMASS_API_BASE 或 http://127.0.0.1:8000",
)
parser.add_argument(
"--interval",
type=float,
default=float(os.environ.get("POLL_INTERVAL", "5")),
help="轮询间隔秒数(默认 POLL_INTERVAL 或 5",
)
args = parser.parse_args()
logger.remove()
logger.add(
sys.stderr,
level=os.environ.get("LOG_LEVEL", "INFO"),
format="<green>{time:YYYY-MM-DD HH:mm:ss.SSS}</green> | "
"<level>{level: <8}</level> | "
"<level>{message}</level>",
)
logger.info(
"biomass_poller 启动 | base={} | interval={}s | GET /api/v1/biomass/real/camera/ 与 /health/result/",
args.base_url.rstrip("/"),
args.interval,
)
asyncio.run(poll_loop(args.base_url, args.interval))
if __name__ == "__main__":
main()