modify: 接口转发
This commit is contained in:
@@ -56,12 +56,12 @@ async def edge_app_controller(payload: dict, topic: str) -> dict | None:
|
||||
)
|
||||
|
||||
if command is None:
|
||||
return _reply(-1, "缺少 command 字段")
|
||||
return None
|
||||
|
||||
handler = _COMMANDS.get(str(command))
|
||||
if handler is None:
|
||||
logger.warning(f"[EdgeAppController] 未知命令: {command}")
|
||||
return _reply(-2, f"未知命令: {command}")
|
||||
return None
|
||||
|
||||
try:
|
||||
data = await handler(payload)
|
||||
@@ -73,7 +73,7 @@ async def edge_app_controller(payload: dict, topic: str) -> dict | None:
|
||||
return data
|
||||
except Exception as 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")
|
||||
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]Expected payload: {payload}")
|
||||
|
||||
if db is None:
|
||||
return _reply(-5, "数据库未初始化")
|
||||
return None
|
||||
if internal_mqtt is None:
|
||||
return _reply(-5, "internal_mqtt 未初始化")
|
||||
return None
|
||||
|
||||
async with db.get_session() as session:
|
||||
from .models.orm import BoardHeartbeat
|
||||
@@ -144,7 +144,7 @@ async def _cmd_alg_media_fetch(payload: dict) -> dict | None:
|
||||
|
||||
if record is None:
|
||||
print(f"[alg_media_fetch]Board {board_id} not found in database.")
|
||||
return _reply(-4, f"设备 {board_id} 不在数据库中")
|
||||
return None
|
||||
|
||||
# 内部请求-响应主题由 [workers.config.internal_mqtt] 配置,与盒子侧约定一致
|
||||
# (盒子只设根主题 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}")
|
||||
|
||||
if resp is None:
|
||||
return _reply(-5, "获取算法媒体信息失败(内部请求无响应)")
|
||||
return None
|
||||
return resp
|
||||
|
||||
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")
|
||||
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]Expected payload: {payload}")
|
||||
|
||||
if db is None:
|
||||
return _reply(-5, "数据库未初始化")
|
||||
return None
|
||||
if internal_mqtt is None:
|
||||
return _reply(-5, "internal_mqtt 未初始化")
|
||||
return None
|
||||
|
||||
async with db.get_session() as session:
|
||||
from .models.orm import BoardHeartbeat
|
||||
@@ -200,7 +200,7 @@ async def _cmd_alg_task_fetch(payload: dict) -> dict | None:
|
||||
|
||||
if record is None:
|
||||
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(
|
||||
|
||||
@@ -38,31 +38,37 @@ logger = logging.getLogger(__name__)
|
||||
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):
|
||||
self._file_path = file_path
|
||||
self._file = None
|
||||
|
||||
async def __aenter__(self) -> "_FileStream":
|
||||
self._file = await aiofiles.open(self._file_path, "rb")
|
||||
def __enter__(self) -> "_FileStream":
|
||||
self._file = open(self._file_path, "rb")
|
||||
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:
|
||||
await self._file.close()
|
||||
self._file.close()
|
||||
self._file = None
|
||||
|
||||
async def __aiter__(self):
|
||||
assert self._file is not None, "必须在 async with 上下文内使用"
|
||||
while chunk := await self._file.read(self._CHUNK_SIZE):
|
||||
yield chunk
|
||||
def read(self, size: int = -1) -> bytes:
|
||||
assert self._file is not None, "必须在 with 上下文内使用"
|
||||
return self._file.read(size)
|
||||
|
||||
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])
|
||||
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(
|
||||
video_url,
|
||||
data={
|
||||
|
||||
Reference in New Issue
Block a user