feat(aiot): 告警冷却时间调整 + 截图本地保留 + 中文路径修复
- 离岗检测冷却时间: 300s → 600s(10分钟) - 入侵检测冷却时间: 120s → 300s(5分钟) - 入侵告警级别改为高(alarm_level=3) - COS 不可用时保留本地截图文件,不再上报后删除 - 修复 cv2.imwrite 中文路径失败,改用 imencode + write_bytes - 配置订阅在 LOCAL 模式下跳过 Redis 连接 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -44,9 +44,14 @@ class PendingCapture:
|
||||
|
||||
class ImageStorageManager:
|
||||
"""图片存储管理器"""
|
||||
|
||||
|
||||
_instance = None
|
||||
_lock = threading.Lock()
|
||||
|
||||
@staticmethod
|
||||
def _sanitize_filename(name: str) -> str:
|
||||
"""清理文件名中的非法字符(/ \\ 等路径分隔符替换为下划线)"""
|
||||
return name.replace("/", "_").replace("\\", "_")
|
||||
|
||||
def __new__(cls, config: Optional[CaptureConfig] = None):
|
||||
if cls._instance is None:
|
||||
@@ -103,17 +108,14 @@ class ImageStorageManager:
|
||||
logger.error(f"图片保存异常: {e}")
|
||||
|
||||
def _save_image(self, capture: PendingCapture) -> Optional[str]:
|
||||
"""保存单张图片"""
|
||||
"""保存单张图片(使用 imencode+write_bytes 避免中文路径问题)"""
|
||||
try:
|
||||
image = capture.image
|
||||
|
||||
|
||||
if image is None:
|
||||
self._failed_count += 1
|
||||
return None
|
||||
|
||||
if len(image.shape) == 3 and image.shape[2] == 3:
|
||||
image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)
|
||||
|
||||
|
||||
if image.shape[1] > self.config.max_width or image.shape[0] > self.config.max_height:
|
||||
scale = min(
|
||||
self.config.max_width / image.shape[1],
|
||||
@@ -124,28 +126,33 @@ class ImageStorageManager:
|
||||
int(image.shape[0] * scale)
|
||||
)
|
||||
image = cv2.resize(image, new_size, interpolation=cv2.INTER_AREA)
|
||||
|
||||
|
||||
date_dir = capture.timestamp.strftime("%Y%m%d")
|
||||
save_dir = Path(self.config.image_dir) / date_dir
|
||||
save_dir.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
filename = f"{capture.camera_id}_{capture.alert_id}{self.config.save_format}"
|
||||
|
||||
safe_camera_id = self._sanitize_filename(capture.camera_id)
|
||||
filename = f"{safe_camera_id}_{capture.alert_id}{self.config.save_format}"
|
||||
filepath = save_dir / filename
|
||||
|
||||
success = cv2.imwrite(
|
||||
str(filepath),
|
||||
|
||||
# 使用 imencode + write_bytes 代替 imwrite,
|
||||
# 因为 cv2.imwrite 在 Windows 上无法处理中文路径
|
||||
success, buffer = cv2.imencode(
|
||||
self.config.save_format,
|
||||
image,
|
||||
[cv2.IMWRITE_JPEG_QUALITY, self.config.quality]
|
||||
)
|
||||
|
||||
|
||||
if success:
|
||||
filepath.write_bytes(buffer.tobytes())
|
||||
self._saved_count += 1
|
||||
logger.debug(f"图片已保存: {filepath}")
|
||||
logger.info(f"图片已保存: {filepath}")
|
||||
return str(filepath)
|
||||
else:
|
||||
logger.warning(f"图片编码失败: {filepath}")
|
||||
self._failed_count += 1
|
||||
return None
|
||||
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"保存图片失败: {e}")
|
||||
self._failed_count += 1
|
||||
@@ -158,20 +165,28 @@ class ImageStorageManager:
|
||||
alert_id: str,
|
||||
timestamp: Optional[datetime] = None
|
||||
) -> Optional[str]:
|
||||
"""异步保存抓拍图片"""
|
||||
"""异步保存抓拍图片,返回预计的文件路径"""
|
||||
ts = timestamp or datetime.now()
|
||||
capture = PendingCapture(
|
||||
image=image,
|
||||
camera_id=camera_id,
|
||||
alert_id=alert_id,
|
||||
timestamp=timestamp or datetime.now()
|
||||
timestamp=ts,
|
||||
)
|
||||
self._save_queue.put(capture)
|
||||
return f"<queued: {alert_id}>"
|
||||
|
||||
# 返回确定性的文件路径(与 _save_image 使用相同的命名规则)
|
||||
date_dir = ts.strftime("%Y%m%d")
|
||||
safe_camera_id = self._sanitize_filename(camera_id)
|
||||
filename = f"{safe_camera_id}_{alert_id}{self.config.save_format}"
|
||||
filepath = Path(self.config.image_dir) / date_dir / filename
|
||||
return str(filepath)
|
||||
|
||||
def get_image_path(self, camera_id: str, alert_id: str) -> Optional[str]:
|
||||
"""获取已保存图片路径"""
|
||||
date_str = datetime.now().strftime("%Y%m%d")
|
||||
filename = f"{camera_id}_{alert_id}{self.config.save_format}"
|
||||
safe_camera_id = self._sanitize_filename(camera_id)
|
||||
filename = f"{safe_camera_id}_{alert_id}{self.config.save_format}"
|
||||
filepath = Path(self.config.image_dir) / date_str / filename
|
||||
|
||||
if filepath.exists():
|
||||
|
||||
Reference in New Issue
Block a user