video.py 8.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194
  1. from __future__ import annotations
  2. from datetime import datetime
  3. from sqlalchemy import (
  4. JSON,
  5. BigInteger,
  6. Boolean,
  7. DateTime,
  8. Float,
  9. ForeignKey,
  10. Integer,
  11. String,
  12. )
  13. from sqlalchemy.orm import Mapped, mapped_column
  14. from app.db.base import Base, TimestampMixin, UUIDPrimaryKeyMixin
  15. # 工业视频平台业务模块:风门 / 局部风机 / 主风机及配电室
  16. VIDEO_MODULE_DOOR = "door"
  17. VIDEO_MODULE_LOCAL_FAN = "local_fan"
  18. VIDEO_MODULE_MAIN_FAN = "main_fan"
  19. VIDEO_MODULES = (VIDEO_MODULE_DOOR, VIDEO_MODULE_LOCAL_FAN, VIDEO_MODULE_MAIN_FAN)
  20. class VideoChannel(UUIDPrimaryKeyMixin, TimestampMixin, Base):
  21. """视频通道:设备树、分屏预览和实时视频对应的摄像机/视频流。
  22. 实时流地址保存在 ``stream_url``,演示视频以磁盘路径保存在 ``file_path``。
  23. """
  24. __tablename__ = "video_channels"
  25. mine_id: Mapped[str] = mapped_column(
  26. String(32), ForeignKey("mines.id", ondelete="CASCADE"), index=True
  27. )
  28. data_source_id: Mapped[str | None] = mapped_column(
  29. String(32),
  30. ForeignKey("data_sources.id", ondelete="SET NULL"),
  31. nullable=True,
  32. index=True,
  33. )
  34. code: Mapped[str] = mapped_column(String(64), unique=True, index=True)
  35. name: Mapped[str] = mapped_column(String(128))
  36. module: Mapped[str] = mapped_column(String(32), index=True)
  37. area_group: Mapped[str] = mapped_column(String(64), default="")
  38. location: Mapped[str] = mapped_column(String(255), default="")
  39. device_type: Mapped[str] = mapped_column(String(32), default="")
  40. point_code: Mapped[str] = mapped_column(String(64), default="")
  41. protocol: Mapped[str] = mapped_column(String(64), default="GB/T 28181")
  42. stream_url: Mapped[str] = mapped_column(String(512), default="")
  43. status: Mapped[str] = mapped_column(String(32), default="online", index=True)
  44. file_path: Mapped[str] = mapped_column(String(512), default="")
  45. file_name: Mapped[str] = mapped_column(String(255), default="")
  46. file_size: Mapped[int] = mapped_column(BigInteger, default=0)
  47. duration_seconds: Mapped[int | None] = mapped_column(Integer, nullable=True)
  48. resolution: Mapped[str] = mapped_column(String(32), default="")
  49. frame_rate: Mapped[int | None] = mapped_column(Integer, nullable=True)
  50. preview_image_path: Mapped[str] = mapped_column(String(512), default="")
  51. is_enabled: Mapped[bool] = mapped_column(Boolean, default=True)
  52. sort_order: Mapped[int] = mapped_column(Integer, default=0)
  53. last_online_at: Mapped[datetime | None] = mapped_column(
  54. DateTime(timezone=True), nullable=True
  55. )
  56. properties: Mapped[dict] = mapped_column(JSON, default=dict)
  57. class VideoSnapshot(UUIDPrimaryKeyMixin, TimestampMixin, Base):
  58. """AI 识别抓拍图像:车辆/人员通行、风门未关严、人员检测等。
  59. ``file_path`` 保存图像在磁盘上的绝对路径,接口按路径读取原图。
  60. """
  61. __tablename__ = "video_snapshots"
  62. mine_id: Mapped[str] = mapped_column(
  63. String(32), ForeignKey("mines.id", ondelete="CASCADE"), index=True
  64. )
  65. channel_id: Mapped[str] = mapped_column(
  66. String(32),
  67. ForeignKey("video_channels.id", ondelete="CASCADE"),
  68. index=True,
  69. )
  70. module: Mapped[str] = mapped_column(String(32), index=True)
  71. snapshot_type: Mapped[str] = mapped_column(String(32), index=True)
  72. event_type: Mapped[str] = mapped_column(String(64), default="", index=True)
  73. level: Mapped[str] = mapped_column(String(16), default="info")
  74. title: Mapped[str] = mapped_column(String(128), default="")
  75. file_path: Mapped[str] = mapped_column(String(512), default="")
  76. file_name: Mapped[str] = mapped_column(String(255), default="")
  77. file_size: Mapped[int] = mapped_column(BigInteger, default=0)
  78. width: Mapped[int | None] = mapped_column(Integer, nullable=True)
  79. height: Mapped[int | None] = mapped_column(Integer, nullable=True)
  80. thumbnail_path: Mapped[str] = mapped_column(String(512), default="")
  81. plate_no: Mapped[str] = mapped_column(String(32), default="")
  82. person_name: Mapped[str] = mapped_column(String(64), default="")
  83. person_count: Mapped[int] = mapped_column(Integer, default=0)
  84. team_name: Mapped[str] = mapped_column(String(64), default="")
  85. gap_mm: Mapped[float | None] = mapped_column(Float, nullable=True)
  86. confidence: Mapped[float | None] = mapped_column(Float, nullable=True)
  87. captured_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), index=True)
  88. properties: Mapped[dict] = mapped_column(JSON, default=dict)
  89. class VideoAnalysisEvent(UUIDPrimaryKeyMixin, TimestampMixin, Base):
  90. """视频智能分析事件与实时告警(防夹人、卡堵、密封失效、违规停留等)。"""
  91. __tablename__ = "video_analysis_events"
  92. mine_id: Mapped[str] = mapped_column(
  93. String(32), ForeignKey("mines.id", ondelete="CASCADE"), index=True
  94. )
  95. channel_id: Mapped[str | None] = mapped_column(
  96. String(32),
  97. ForeignKey("video_channels.id", ondelete="SET NULL"),
  98. nullable=True,
  99. index=True,
  100. )
  101. snapshot_id: Mapped[str | None] = mapped_column(
  102. String(32),
  103. ForeignKey("video_snapshots.id", ondelete="SET NULL"),
  104. nullable=True,
  105. index=True,
  106. )
  107. module: Mapped[str] = mapped_column(String(32), index=True)
  108. event_type: Mapped[str] = mapped_column(String(64), index=True)
  109. category: Mapped[str] = mapped_column(String(32), default="")
  110. level: Mapped[str] = mapped_column(String(16), default="info", index=True)
  111. title: Mapped[str] = mapped_column(String(128))
  112. description: Mapped[str] = mapped_column(String(512), default="")
  113. confidence: Mapped[float | None] = mapped_column(Float, nullable=True)
  114. status: Mapped[str] = mapped_column(String(32), default="pending", index=True)
  115. occurred_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), index=True)
  116. handled_at: Mapped[datetime | None] = mapped_column(
  117. DateTime(timezone=True), nullable=True
  118. )
  119. handled_by: Mapped[str] = mapped_column(String(64), default="")
  120. handle_remark: Mapped[str] = mapped_column(String(255), default="")
  121. properties: Mapped[dict] = mapped_column(JSON, default=dict)
  122. class VideoRecord(UUIDPrimaryKeyMixin, TimestampMixin, Base):
  123. """通行与操作记录:三大模块底部记录面板的统一宽表。
  124. ``record_type`` 取值:
  125. ``vehicle_passage`` 车辆通行、``person_passage`` 人员通行、``door_unclosed`` 风门未关严、
  126. ``person_track`` 人员轨迹、``area_stay`` 区域统计、``person_warn`` 预警记录、
  127. ``person_detect`` 人员检测、``device_operation`` 设备操作、``device_warn`` 设备预警。
  128. """
  129. __tablename__ = "video_records"
  130. mine_id: Mapped[str] = mapped_column(
  131. String(32), ForeignKey("mines.id", ondelete="CASCADE"), index=True
  132. )
  133. channel_id: Mapped[str] = mapped_column(
  134. String(32),
  135. ForeignKey("video_channels.id", ondelete="CASCADE"),
  136. index=True,
  137. )
  138. snapshot_id: Mapped[str | None] = mapped_column(
  139. String(32),
  140. ForeignKey("video_snapshots.id", ondelete="SET NULL"),
  141. nullable=True,
  142. index=True,
  143. )
  144. module: Mapped[str] = mapped_column(String(32), index=True)
  145. record_type: Mapped[str] = mapped_column(String(32), index=True)
  146. device_name: Mapped[str] = mapped_column(String(128), default="")
  147. area_name: Mapped[str] = mapped_column(String(64), default="")
  148. location: Mapped[str] = mapped_column(String(255), default="")
  149. plate_no: Mapped[str] = mapped_column(String(32), default="")
  150. person_name: Mapped[str] = mapped_column(String(64), default="")
  151. person_count: Mapped[int] = mapped_column(Integer, default=0)
  152. team_name: Mapped[str] = mapped_column(String(64), default="")
  153. operation_type: Mapped[str] = mapped_column(String(64), default="")
  154. compare_result: Mapped[str] = mapped_column(String(64), default="")
  155. warn_type: Mapped[str] = mapped_column(String(64), default="")
  156. result: Mapped[str] = mapped_column(String(64), default="")
  157. handle_status: Mapped[str] = mapped_column(String(32), default="")
  158. device_status: Mapped[str] = mapped_column(String(32), default="")
  159. level: Mapped[str] = mapped_column(String(16), default="")
  160. gap_mm: Mapped[float | None] = mapped_column(Float, nullable=True)
  161. stay_seconds: Mapped[int | None] = mapped_column(Integer, nullable=True)
  162. enter_time: Mapped[datetime | None] = mapped_column(
  163. DateTime(timezone=True), nullable=True
  164. )
  165. leave_time: Mapped[datetime | None] = mapped_column(
  166. DateTime(timezone=True), nullable=True
  167. )
  168. occurred_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), index=True)
  169. properties: Mapped[dict] = mapped_column(JSON, default=dict)