modify: 接口转发

This commit is contained in:
2026-08-11 14:47:19 +08:00
parent 74d115d2bb
commit 3e1c568d04
2 changed files with 34 additions and 26 deletions
+12 -12
View File
@@ -56,12 +56,12 @@ async def edge_app_controller(payload: dict, topic: str) -> dict | None:
) )
if command is None: if command is None:
return _reply(-1, "缺少 command 字段") return None
handler = _COMMANDS.get(str(command)) handler = _COMMANDS.get(str(command))
if handler is None: if handler is None:
logger.warning(f"[EdgeAppController] 未知命令: {command}") logger.warning(f"[EdgeAppController] 未知命令: {command}")
return _reply(-2, f"未知命令: {command}") return None
try: try:
data = await handler(payload) data = await handler(payload)
@@ -73,7 +73,7 @@ async def edge_app_controller(payload: dict, topic: str) -> dict | None:
return data return data
except Exception as e: except Exception as e:
logger.error(f"[EdgeAppController] 命令 {command} 执行失败: {e}") logger.error(f"[EdgeAppController] 命令 {command} 执行失败: {e}")
return _reply(-3, f"命令执行异常: {e}") return None
# ============================================================ # ============================================================
@@ -124,15 +124,15 @@ async def _cmd_alg_media_fetch(payload: dict) -> dict | None:
board_id = payload.get("BoardId") board_id = payload.get("BoardId")
if not board_id: if not board_id:
return _reply(-4, "缺少 BoardId 参数") return None
print(f"[alg_media_fetch]Processing for board: {board_id}") print(f"[alg_media_fetch]Processing for board: {board_id}")
print(f"[alg_media_fetch]Expected payload: {payload}") print(f"[alg_media_fetch]Expected payload: {payload}")
if db is None: if db is None:
return _reply(-5, "数据库未初始化") return None
if internal_mqtt is None: if internal_mqtt is None:
return _reply(-5, "internal_mqtt 未初始化") return None
async with db.get_session() as session: async with db.get_session() as session:
from .models.orm import BoardHeartbeat from .models.orm import BoardHeartbeat
@@ -144,7 +144,7 @@ async def _cmd_alg_media_fetch(payload: dict) -> dict | None:
if record is None: if record is None:
print(f"[alg_media_fetch]Board {board_id} not found in database.") print(f"[alg_media_fetch]Board {board_id} not found in database.")
return _reply(-4, f"设备 {board_id} 不在数据库中") return None
# 内部请求-响应主题由 [workers.config.internal_mqtt] 配置,与盒子侧约定一致 # 内部请求-响应主题由 [workers.config.internal_mqtt] 配置,与盒子侧约定一致
# (盒子只设根主题 box,子路径固定 aibox_sophon/...,回复主题固定为 ..._reply)。 # (盒子只设根主题 box,子路径固定 aibox_sophon/...,回复主题固定为 ..._reply)。
@@ -161,7 +161,7 @@ async def _cmd_alg_media_fetch(payload: dict) -> dict | None:
print(f"[alg_media_fetch]Response from internal_mqtt: {resp}") print(f"[alg_media_fetch]Response from internal_mqtt: {resp}")
if resp is None: if resp is None:
return _reply(-5, "获取算法媒体信息失败(内部请求无响应)") return None
return resp return resp
async def _cmd_alg_task_fetch(payload: dict) -> dict | None: async def _cmd_alg_task_fetch(payload: dict) -> dict | None:
@@ -180,15 +180,15 @@ async def _cmd_alg_task_fetch(payload: dict) -> dict | None:
board_id = payload.get("BoardId") board_id = payload.get("BoardId")
if not board_id: if not board_id:
return _reply(-4, "缺少 BoardId 参数") return None
print(f"[alg_task_fetch]Processing for board: {board_id}") print(f"[alg_task_fetch]Processing for board: {board_id}")
print(f"[alg_task_fetch]Expected payload: {payload}") print(f"[alg_task_fetch]Expected payload: {payload}")
if db is None: if db is None:
return _reply(-5, "数据库未初始化") return None
if internal_mqtt is None: if internal_mqtt is None:
return _reply(-5, "internal_mqtt 未初始化") return None
async with db.get_session() as session: async with db.get_session() as session:
from .models.orm import BoardHeartbeat from .models.orm import BoardHeartbeat
@@ -200,7 +200,7 @@ async def _cmd_alg_task_fetch(payload: dict) -> dict | None:
if record is None: if record is None:
print(f"[alg_task_fetch]Board {board_id} not found in database.") print(f"[alg_task_fetch]Board {board_id} not found in database.")
return _reply(-4, f"设备 {board_id} 不在数据库中") return None
resp = await internal_mqtt.request( resp = await internal_mqtt.request(
+22 -14
View File
@@ -38,31 +38,37 @@ logger = logging.getLogger(__name__)
router = APIRouter(prefix="/aibox_sophon_adapter", tags=["aibox_sophon_adapter"]) router = APIRouter(prefix="/aibox_sophon_adapter", tags=["aibox_sophon_adapter"])
class _FileStream(httpx.AsyncByteStream): class _FileStream:
""" """
异步文件流 — 用于流式上传视频,避免一次性将整个文件读入内存。 文件流 — 用于流式上传视频,避免一次性将整个文件读入内存。
以 1MB 分块异步读取文件,供 httpx multipart 上传使用。 注意: httpx 0.28 的 multipart 上传(FileField)要求文件对象提供同步的
read()/seek() 方法,httpx 内部以 64KB 分块同步读取;AsyncByteStream 只能
作为整个请求体 content= 使用,无法与 multipart 字段共存。因此这里提供
同步文件接口,配合同步上下文管理器 with 使用(本地磁盘小分块读取,
内存占用依然有界)。
""" """
_CHUNK_SIZE = 1024 * 1024
def __init__(self, file_path: str): def __init__(self, file_path: str):
self._file_path = file_path self._file_path = file_path
self._file = None self._file = None
async def __aenter__(self) -> "_FileStream": def __enter__(self) -> "_FileStream":
self._file = await aiofiles.open(self._file_path, "rb") self._file = open(self._file_path, "rb")
return self return self
async def __aexit__(self, exc_type, exc, tb) -> None: def __exit__(self, exc_type, exc, tb) -> None:
if self._file is not None: if self._file is not None:
await self._file.close() self._file.close()
self._file = None
async def __aiter__(self): def read(self, size: int = -1) -> bytes:
assert self._file is not None, "必须在 async with 上下文内使用" assert self._file is not None, "必须在 with 上下文内使用"
while chunk := await self._file.read(self._CHUNK_SIZE): return self._file.read(size)
yield chunk
def seek(self, offset: int, whence: int = 0) -> int:
assert self._file is not None, "必须在 with 上下文内使用"
return self._file.seek(offset, whence)
# ============================================================ # ============================================================
@@ -206,7 +212,9 @@ async def _upload_video_to_external(
video_path = os.path.join(video_dir_path, video_files[0]) video_path = os.path.join(video_dir_path, video_files[0])
try: try:
async with _FileStream(video_path) as video_stream: # httpx 0.28 的 multipart 以同步 read() 流式读取文件(64KB 分块),
# 因此这里用同步 with 打开文件流
with _FileStream(video_path) as video_stream:
resp = await http_client.post( resp = await http_client.post(
video_url, video_url,
data={ data={