| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135 |
- """
- 人体检测 SQLite 读取模块
- 读取 YOLO 人体检测结果,判断是否在冷却期内
- 用于抑制异常报警:5 分钟内检测到人 → 不报警
- """
- import sqlite3
- from datetime import datetime, timedelta
- from pathlib import Path
- from typing import Optional
- import logging
- logger = logging.getLogger('PickupMonitor')
- class HumanDetectionReader:
- """
- 人体检测结果读取器
-
- 只要任意摄像头在指定时间窗口内检测到人,就返回 True(抑制报警)
- """
-
- def __init__(self, db_path: str, cooldown_minutes: int = 5):
- """
- 初始化读取器
-
- 参数:
- db_path: SQLite 数据库路径
- cooldown_minutes: 冷却时间(分钟),检测到人后多久内抑制报警
- """
- self.db_path = Path(db_path) if db_path else None
- self.cooldown_minutes = cooldown_minutes
-
- # 缓存:避免频繁查询数据库
- self._cache_result: Optional[bool] = None
- self._cache_time: Optional[datetime] = None
- self._cache_ttl_seconds = 10 # 缓存有效期(秒)
-
- def _get_connection(self) -> Optional[sqlite3.Connection]:
- """
- 获取数据库连接
-
- 返回:
- 连接对象,数据库不存在时返回 None
- """
- if self.db_path is None or not self.db_path.exists():
- return None
-
- try:
- conn = sqlite3.connect(str(self.db_path), timeout=5)
- return conn
- except Exception as e:
- logger.warning(f"人体检测数据库连接失败: {e}")
- return None
-
- def get_last_person_time(self) -> Optional[datetime]:
- """
- 获取最后一次检测到人的时间(任意摄像头)
-
- 返回:
- 最后检测到人的时间,无数据返回 None
- """
- conn = self._get_connection()
- if conn is None:
- return None
-
- try:
- row = conn.execute("""
- SELECT datetime FROM detections
- WHERE detected = 1
- ORDER BY datetime DESC
- LIMIT 1
- """).fetchone()
- conn.close()
-
- if row:
- # 解析时间字符串
- return datetime.strptime(row[0], "%Y-%m-%d %H:%M:%S")
- return None
-
- except Exception as e:
- logger.warning(f"查询人体检测数据失败: {e}")
- if conn:
- conn.close()
- return None
-
- def is_in_cooldown(self) -> bool:
- """
- 判断是否在冷却期内(任意摄像头检测到人后的 N 分钟内)
-
- 返回:
- True: 在冷却期内,应抑制报警
- False: 不在冷却期,可正常报警
- """
- now = datetime.now()
-
- # 检查缓存是否有效
- if (self._cache_result is not None and
- self._cache_time is not None and
- (now - self._cache_time).total_seconds() < self._cache_ttl_seconds):
- return self._cache_result
-
- # 查询数据库
- last_person_time = self.get_last_person_time()
-
- if last_person_time is None:
- result = False # 从未检测到人,不抑制
- else:
- # 判断是否在冷却期内
- cooldown_end = last_person_time + timedelta(minutes=self.cooldown_minutes)
- result = now < cooldown_end
-
- # 更新缓存
- self._cache_result = result
- self._cache_time = now
-
- return result
-
- def get_status_info(self) -> str:
- """
- 获取状态信息(用于日志)
-
- 返回:
- 状态描述字符串
- """
- last_time = self.get_last_person_time()
- if last_time is None:
- return "无人体检测数据"
-
- elapsed = (datetime.now() - last_time).total_seconds() / 60
- if elapsed < self.cooldown_minutes:
- remaining = self.cooldown_minutes - elapsed
- return f"冷却中(剩余{remaining:.1f}分钟)"
- else:
- return f"已超时({elapsed:.1f}分钟前有人)"
|