modify: sophon 视频文件上传、告警接口

This commit is contained in:
2026-07-28 14:16:00 +08:00
parent 79eb7fe5cc
commit 320f6c19a2
17 changed files with 575 additions and 52 deletions
+17 -34
View File
@@ -1,45 +1,28 @@
"""
模型导出 — aibox_sophon_adapter
包含 gen_models 自动生成的模型和手动定义的 board_models。
自动生成,请勿手动修改
"""
from .pydantic import (
AiboxSophonAdapterResponse,
GetDeviceGetRequest,
GetDeviceGetResponse,
GetOfflineDevicesGetResponse,
ListDevicesGetResponse,
)
from .board_models import (
BoardPingRequest,
HostDiskInfo,
MediaInfo,
MediaStatusInfo,
AlgItemInfo,
AlgTaskStatusInfo,
TaskInfo,
TpuInfo,
DeviceSummary,
)
from .orm import BoardHeartbeat
from .pydantic import AiboxSophonAdapterResponse
from .pydantic import AlarmReportPostResponse
from .pydantic import GetAlarmGetRequest
from .pydantic import GetAlarmGetResponse
from .pydantic import GetDeviceGetRequest
from .pydantic import GetDeviceGetResponse
from .pydantic import GetOfflineDevicesGetResponse
from .pydantic import ListAlarmsGetRequest
from .pydantic import ListAlarmsGetResponse
from .pydantic import ListDevicesGetResponse
__all__ = [
# 自动生成
"AiboxSophonAdapterResponse",
"AlarmReportPostResponse",
"GetAlarmGetRequest",
"GetAlarmGetResponse",
"GetDeviceGetRequest",
"GetDeviceGetResponse",
"GetOfflineDevicesGetResponse",
"ListAlarmsGetRequest",
"ListAlarmsGetResponse",
"ListDevicesGetResponse",
# 手动定义
"BoardPingRequest",
"HostDiskInfo",
"MediaInfo",
"MediaStatusInfo",
"AlgItemInfo",
"AlgTaskStatusInfo",
"TaskInfo",
"TpuInfo",
"DeviceSummary",
# ORM
"BoardHeartbeat",
]
+139
View File
@@ -0,0 +1,139 @@
"""
告警上报 — Pydantic 数据模型
包含请求体和响应体。请求体声明了规范中所有已知字段,
未声明的额外字段通过 model_config extra=allow 保留。
"""
from typing import Any, Optional
from pydantic import BaseModel, Field
# ============================================================
# 告警报文子结构
# ============================================================
class GpsInfo(BaseModel):
"""GPS 位置信息"""
available: bool = Field(default=False, description="是否有 GPS 功能")
kSpeed: float = Field(default=0.0, description="对地速度 (km/h)")
nSpeed: float = Field(default=0.0, description="对地速率 (节)")
latitude: float = Field(default=0.0, description="纬度")
longitude: float = Field(default=0.0, description="经度")
angleCourse: float = Field(default=0.0, description="GPS 角度")
class MediaInfo(BaseModel):
"""视频通道信息"""
MediaName: str = Field(default="", description="通道标识")
MediaDesc: str = Field(default="", description="通道描述")
MediaUrl: str = Field(default="", description="通道视频流地址")
MediaWidth: int = Field(default=0, description="通道视频宽度")
MediaHeight: int = Field(default=0, description="通道视频高度")
SubId: str = Field(default="", description="关联 GB28181 通道号")
GBTransport: bool = Field(default=False, description="是否接入 GB28181")
RtspTransport: bool = Field(default=False, description="是否开启 RTSP 转发")
Params: Optional[dict] = Field(default=None, description="预留其他参数")
class ResultProperty(BaseModel):
"""告警 Result 中的属性项"""
property: str = Field(default="", description="属性标识")
desc: str = Field(default="", description="属性描述")
value: Any = Field(default=None, description="属性值(String/Int/Float)")
display: str = Field(default="", description="属性详细信息")
type: str = Field(default="", description="属性值类型")
class AlarmResult(BaseModel):
"""违规检测结果(结构随算法变化,extra=allow 兜底)"""
model_config = {"extra": "allow"}
Type: str = Field(default="", description="违规类型标识(NoHelmet / Intrude / PeopleCross 等)")
Description: str = Field(default="", description="违规描述")
Cropped: bool = Field(default=False, description="是否裁剪")
RegType: str = Field(default="Rectangle", description="上报点集类型(Rectangle / Line / Polygon)")
RelativeBox: list[float] = Field(default_factory=list, description="矩形目标位置 [X,Y,Width,Height] 百分比")
RelativeRegion: list[dict] = Field(default_factory=list, description="多边形目标位置")
Properties: list[ResultProperty] = Field(default_factory=list, description="算法特殊属性")
# ============================================================
# 告警上报请求体
# ============================================================
class AlarmReportRequest(BaseModel):
"""
告警上报请求体 — 声明规范中所有已知字段
未声明的额外字段通过 extra=allow 保留,完整数据可通过 model_dump() 获取。
盒子检测到违规事件后,先上传视频再上报此 JSON 告警报文。
"""
model_config = {"extra": "allow"}
# 设备信息
BoardIp: str = Field(default="", description="盒子网络地址")
BoardId: str = Field(default="", description="盒子唯一标识")
# 告警标识
AlarmId: str = Field(default="", description="告警标识")
UniqueId: str = Field(default="", description="告警唯一标识(与 AlarmId 等价)")
# 透传字段
Addition: Optional[dict] = Field(default=None, description="透传字段")
# 截图数据
ImageData: str = Field(default="", description="无标注告警图 JPEG Base64 编码")
ImageDataLabeled: str = Field(default="", description="有标注告警图 JPEG Base64 编码")
# 截图本地路径
LocalRawPath: str = Field(default="", description="无标注告警图在盒子中的相对路径")
LocalLabeledPath: str = Field(default="", description="有标注告警图在盒子中的相对路径")
# 通道信息
Media: MediaInfo = Field(default_factory=MediaInfo, description="配置的媒体通道")
# 违规描述
Summary: str = Field(default="", description="违规类型(NoHelmet / Intrude / PeopleCross 等)")
# 任务信息
TaskSession: str = Field(default="", description="任务编号")
GBDeviceId: str = Field(default="", description="国标设备编号,不存在则为空字符串")
GBTaskChnId: str = Field(default="", description="任务对应的国标通道编号,不存在则为空字符串")
TaskDesc: str = Field(default="", description="任务描述")
# 时间
Time: str = Field(default="", description="告警时间戳字符串")
TimeStamp: int = Field(default=0, description="告警时间戳(微秒)")
# 视频
VideoFile: str = Field(default="", description="关联的视频标识(视频上传接口返回)")
# GPS
GPS: GpsInfo = Field(default_factory=GpsInfo, description="当前位置信息")
# 违规结果
Result: AlarmResult = Field(default_factory=AlarmResult, description="违规检测结果")
# 预留字段
Type: int = Field(default=0, description="预留字段")
DataId: int = Field(default=0, description="数据 ID")
Id: int = Field(default=0, description="记录 ID")
# ============================================================
# 告警上报响应体
# ============================================================
class AlarmReportResult(BaseModel):
"""告警上报接口返回状态"""
Code: int = Field(default=0, description="接口状态错误码,0 表示成功,非 0 表示失败")
Desc: str = Field(default="", description="错误码描述")
class AlarmReportResponse(BaseModel):
"""告警上报接口完整响应
仅当 HTTP 200 + Code 非 0 时,盒子认为上报失败。
"""
Result: AlarmReportResult = Field(default_factory=AlarmReportResult, description="接口状态返回")
+1 -1
View File
@@ -45,7 +45,7 @@ class AlgItemInfo(BaseModel):
lineRequired: bool = Field(default=False, description="是否需要线")
majorId: int = Field(..., description="主 ID")
majorOnly: bool = Field(default=False, description="仅主算法")
minorId: int = Field(..., description="副 ID")
minorId: int = Field(default=0, description="副 ID(majorOnly=true 时可能不传)")
name: str = Field(..., description="算法名称")
zoneDesc: str = Field(default="", description="区域描述")
zoneRequired: bool = Field(default=False, description="是否需要区域")
+19 -1
View File
@@ -11,7 +11,7 @@ from sqlmodel import Field, SQLModel
class BoardHeartbeat(SQLModel, table=True):
"""设备心跳记录表"""
__tablename__ = "board_heartbeat"
__tablename__ = "board_heartbeat" # type: ignore
id: Optional[int] = Field(default=None, primary_key=True)
board_id: str = Field(index=True, unique=True, description="设备唯一标识 (BoardId)")
@@ -29,3 +29,21 @@ class BoardHeartbeat(SQLModel, table=True):
is_online: bool = Field(default=True, description="是否在线")
created_at: datetime = Field(default_factory=datetime.now, description="首次上报时间")
updated_at: datetime = Field(default_factory=datetime.now, description="最后更新时间")
class AlarmRecord(SQLModel, table=True):
"""告警记录表 — 存储盒子上报的完整告警报文"""
__tablename__ = "alarm_record" # type: ignore
id: Optional[int] = Field(default=None, primary_key=True)
board_id: str = Field(index=True, description="设备唯一标识 (BoardId)")
board_ip: str = Field(default="", description="设备 IP")
alarm_id: str = Field(index=True, unique=True, description="告警唯一标识 (AlarmId/UniqueId)")
summary: str = Field(default="", description="违规类型 (Summary)")
task_session: str = Field(default="", description="任务编号 (TaskSession)")
media_name: str = Field(default="", description="通道名称")
video_file: str = Field(default="", description="关联视频标识 (VideoFile)")
alarm_time: str = Field(default="", description="告警时间 (Time)")
alarm_timestamp: int = Field(default=0, description="告警时间戳-微秒 (TimeStamp)")
alarm_data: str = Field(default="{}", description="完整告警报文 JSON 字符串")
created_at: datetime = Field(default_factory=datetime.now, description="上报时间")
+37 -1
View File
@@ -8,6 +8,28 @@ from __future__ import annotations
from typing import Any, Optional
from pydantic import BaseModel, Field
class AlarmReportPostResponse(BaseModel):
"""告警上报接口 — 响应体"""
Result: dict[str, Any] = Field(...)
class GetAlarmGetRequest(BaseModel):
"""查询单条告警详情 — 请求体"""
alarm_id: str = Field(...)
class GetAlarmGetResponse(BaseModel):
"""查询单条告警详情 — 响应体"""
alarm_id: str = Field(...)
board_id: str = Field(...)
board_ip: str = Field(...)
summary: str = Field(...)
task_session: str = Field(...)
media_name: str = Field(...)
video_file: str = Field(...)
alarm_time: str = Field(...)
alarm_timestamp: int = Field(...)
alarm_data: dict[str, Any] = Field(...)
created_at: str = Field(...)
class GetDeviceGetRequest(BaseModel):
"""查询单个设备详情 — 请求体"""
board_id: str = Field(...)
@@ -24,6 +46,20 @@ class GetOfflineDevicesGetResponse(BaseModel):
offline_devices: list[dict] = Field(...)
total: int = Field(...)
class ListAlarmsGetRequest(BaseModel):
"""查询告警记录列表 — 请求体"""
board_id: str = Field(...)
summary: str = Field(...)
page: int = Field(...)
page_size: int = Field(...)
class ListAlarmsGetResponse(BaseModel):
"""查询告警记录列表 — 响应体"""
alarms: list[dict] = Field(...)
total: int = Field(...)
page: int = Field(...)
page_size: int = Field(...)
class ListDevicesGetResponse(BaseModel):
"""查询所有设备状态 — 响应体"""
devices: list[dict] = Field(...)
@@ -31,4 +67,4 @@ class ListDevicesGetResponse(BaseModel):
# 所有响应模型的联合类型
AiboxSophonAdapterResponse = ListDevicesGetResponse | GetOfflineDevicesGetResponse | GetDeviceGetResponse
AiboxSophonAdapterResponse = GetOfflineDevicesGetResponse | AlarmReportPostResponse | GetAlarmGetResponse | GetDeviceGetResponse | ListAlarmsGetResponse | ListDevicesGetResponse
@@ -0,0 +1,16 @@
"""
视频上传 — Pydantic 数据模型
"""
from pydantic import BaseModel, Field
class VideoUploadResult(BaseModel):
"""视频上传接口返回状态"""
Code: int = Field(default=0, description="接口状态错误码,0 表示成功")
Desc: str = Field(default="", description="错误码描述")
class VideoUploadResponse(BaseModel):
"""视频上传接口完整响应"""
Result: VideoUploadResult = Field(default_factory=VideoUploadResult, description="接口状态返回")
VideoId: str = Field(..., description="视频唯一标识")