modify: sophon 视频文件上传、告警接口
This commit is contained in:
@@ -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",
|
||||
]
|
||||
|
||||
@@ -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="接口状态返回")
|
||||
|
||||
@@ -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="是否需要区域")
|
||||
|
||||
@@ -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="上报时间")
|
||||
|
||||
@@ -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="视频唯一标识")
|
||||
Reference in New Issue
Block a user