#!/usr/bin/env python3
"""
独立进程:轮询 Fish API 的两个结果接口,用 loguru 输出响应。
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
_last_camera: dict | None = None
_last_health: dict | None = None
def _has_data(body: Any) -> bool:
"""非空业务数据:code==200 且 data 里至少有一个非空字段。"""
if not isinstance(body, dict):
return False
if body.get("code") != 200:
return True # 错误也算"新信息"
data = body.get("data", {})
if not isinstance(data, dict):
return bool(data)
return any(bool(v) for v in data.values())
async def poll_once(client: httpx.AsyncClient, base: str) -> None:
global _last_camera, _last_health
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)
changed1 = b1 != _last_camera
changed2 = b2 != _last_health
if changed1 and _has_data(b1):
logger.info("[real/camera/] HTTP {} | {}", r1.status_code, json.dumps(b1, ensure_ascii=False))
if changed2 and _has_data(b2):
logger.info("[health/result/] HTTP {} | {}", r2.status_code, json.dumps(b2, ensure_ascii=False))
_last_camera = b1
_last_health = b2
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="{time:YYYY-MM-DD HH:mm:ss.SSS} | "
"{level: <8} | "
"{message}",
)
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()