Files
fire_leave_tool/docs/业务逻辑梳理.md
2026-05-12 15:45:50 +08:00

529 lines
15 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 动火离人微服务业务逻辑梳理
本文档基于当前代码整理,目标是帮助快速回忆这个微服务现在“实际在做什么”,以及哪些逻辑是设计上存在但当前代码里已经弱化或未真正生效的。
## 1. 项目定位
当前仓库是“动火离人”项目中的微服务管理程序,职责不是前端展示,也不是后端总控,而是:
- 接收后端/平台下发的设备配置
- 为每个设备启动并管理本地推理程序
- 接收推理结果和测温结果
- 按规则判断是否触发报警
- 回溯报警前后视频,拼接并上传
- 通过 WebSocket 将报警事件和数据快照发给后端,再由后端转给前端
整体链路可以理解为:
`前端配置 -> 后端下发配置 -> 微服务启动/管理推理 -> 推理结果回流微服务 -> 微服务判定报警 -> 上传视频 + websocket上报 -> 后端转发前端展示`
## 2. 当前代码里的三类核心进程
每个设备启动时,实际上会拉起 3 条本地能力:
1. `fire_leave` 推理程序
- 模型参数:`./yolov5 -m model`
- 主要用途:统计人员数量
- 结果通过本地 HTTP 服务回传
2. `fire_check` 推理程序
- 模型参数:`./yolov5 -m fire_check -R`
- 主要用途:检测火焰
- 结果通过另一套本地 HTTP 服务回传
3. 测温程序
- 启动脚本:`./release/run_thermometry.sh`
- 结果通过本地 TCP 服务回传
也就是说,当前“动火离人”并不是单模型,而是:
- 一套模型做人数量判断
- 一套模型做火焰检测
- 一套测温程序提供温度值
## 3. 配置来源与初始化
### 3.1 启动时配置来源
程序启动后先从 `/usr/local/etc/service.conf` 读取设备配置。
配置结构已经是多设备形式,核心字段包括:
- `device_uuid`
- `task_id`
- `camera_rtsp`
- `camera_ip`
- `confidence`
- `detect_area.fire_leave`
- `detect_area.fire_check`
- `param.fire_leave`
- `param.fire_check`
其中:
- `fire_leave` 检测区给人员检测使用
- `fire_check` 检测区给火焰检测使用
- 两套检测区彼此独立
### 3.2 配置落盘
初始化后,每个设备还会落盘到:
- `/data/devices/device_{deviceUUID}.json`
用于后续读取和状态持久化。
## 4. 每个设备启动时的完整流程
`main.go` 中每个设备会进入 `processDevice()`,当前流程如下:
1. 设备加入全局 `DeviceManager`
2.`fire_leave` 分配一个端口
3.`fire_check` 再分配一个端口
4. 分别启动两个本地 HTTP 接收服务
5. 等待 HTTP `/health` 就绪
6. 把前端配置的检测区域转换成推理程序需要的点串格式
7. 启动 `fire_leave` 推理进程
8. 启动 `fire_check` 推理进程
9. 启动测温程序
10. 等待测温 TCP 服务就绪
11. 创建 TCP 客户端读取温度
12. 进入主循环,持续处理:
- `fire_leave` 推理结果
- `fire_check` 推理结果
- 周期性温度读取和报警判定
## 5. 两条检测链路的当前职责
## 5.1 fire_leave 链路
当前代码里,`fire_leave` 推理结果主要只负责更新人数。
处理逻辑:
- 微服务接收 `/video/post`
- 解析 `params`
-`ClassIdx == 1`
-`Number` 作为当前人数 `PersonCount`
- 写回设备状态
也就是说,当前“离人”判断本身不是由模型直接报“报警/不报警”,而是:
- 模型只提供人数
- 微服务结合温度再做二次判定
## 5.2 fire_check 链路
当前代码里,`fire_check` 推理结果专门负责火焰报警。
处理逻辑:
- 微服务接收 `fire_check``/video/post`
- 遍历结果
-`ClassIdx == 0 && Number > 0` 时,认为检测到火焰
- 连续满足 `detection_time` 秒后触发火焰报警
火焰报警完全独立于“无人 + 高温”逻辑,不依赖温度和人数。
## 6. 动火离人报警的当前判定逻辑
这是你现在代码里的主报警逻辑,运行在定时器分支里,每 2 秒检查一次。
### 6.1 输入条件
判定使用两个实时量:
- `PersonCount == 0`
- `Temperature > TemperatureThreshold`
其中温度还做了一个简化的防抖:
- 高于阈值 `+0.5`:稳定超温
- 低于阈值 `-0.5`:稳定未超温
- 落在中间:按当前是否大于阈值判断
### 6.2 触发条件
只有同时满足以下条件才会计时:
- 无人
- 温度超阈值
满足后开始累计持续时间,达到 `DetectionTime` 后触发报警。
默认值:
- `DetectionTime` 默认 `20`
- `AlarmTime` 默认 `300`
### 6.3 冷却时间
报警触发后会进入冷却:
- `AlarmPauseUntil = now + AlarmTime`
冷却期内,即使条件继续满足,也不会重复触发。
### 6.4 当前本质
当前“动火离人报警”实际规则可以概括成一句话:
`人员检测结果为 0 + 测温超过阈值 + 持续达到设定秒数 -> 触发 fire_leave 报警`
## 7. 火焰检测报警的当前判定逻辑
火焰检测走另一套状态机。
### 7.1 输入条件
- `fire_check` 结果中出现 `ClassIdx == 0 && Number > 0`
### 7.2 持续时间
持续时间取自:
- `deviceData.FireCheckParam.DetectionTime`
默认值:
- 如果没配,默认 `2`
### 7.3 触发结果
持续检测到火焰达到时长后,触发:
- `GlobalReporter.TriggerFireCheckAlarm(deviceData, alarmTime)`
### 7.4 当前本质
当前“火焰检测报警”可以概括成:
`火焰模型连续检测到火焰达到设定秒数 -> 触发 fire_check 报警`
## 8. 报警后做了什么
无论是 `fire_leave` 还是 `fire_check`,触发后总体都会走这几步:
1. 防止同一告警重复处理
2. 去录像目录找报警前后视频
3. 拼接成一个完整 MP4
4. 复制到 `/data/upload`
5. 上传到 Nginx/后端文件接口
6. 通过 WebSocket 上报事件消息
7. 再上报一份数据快照消息
## 9. 视频回溯与拼接逻辑
当前录像目录:
- 普通动火离人视频:`/usr/data/camera/{deviceUUID}`
- 火焰检测视频:`/usr/data/camera/{deviceUUID}_fire_check`
拼接逻辑:
1. 先把报警时间对齐到最近的 10 秒片段
2.`前10秒``当前10秒``后10秒` 三段视频
3. 如果缺段,会从同目录找最接近的 mp4 作为替代
4. 至少要找到 2 段视频才继续
5.`mp4_merge` 进行拼接
6. 输出到 `/data/upload/{deviceUUID}-{日期-时间}.MP4`
所以当前上报视频本质上是“报警时刻前后约 30 秒的回溯片段”。
## 10. WebSocket 在当前系统里的作用
WebSocket 地址写死为:
- `ws://172.17.0.1:18080/ws`
主要承担 4 件事:
1. 服务注册
2. 订阅平台 topic
3. 接收配置更新
4. 上报事件与数据
### 10.1 启动后注册
启动后会注册一个服务:
- `containerName = fireleave-container`
- `serviceID = fireleave-service-{timestamp}`
### 10.2 订阅的 topic
当前代码里会订阅:
- `/dhlr/alert`
- `/dhlr/forbid_time`
### 10.3 当前业务上真正需要接收的不是设备配置
这里要和“设备配置来源”明确区分。
设备基础配置当前应以本地文件为准:
- `/usr/local/etc/service.conf`
也就是说:
- 摄像头
- 检测区域
- 置信度
- 各类基础参数
这些设备配置不是依赖 WebSocket 实时下发来生效,而是直接读取本地 `service.conf`
WebSocket 在这块更应该承担的是“控制类消息”的接收,例如:
- 报警暂停时长
- 延迟报警/允许离岗时长
- 声光报警控制
补充说明:
- 当前代码里 `ws_channel.go` 仍保留了把 `publish.data` 解析成 `DeviceData` 并写回本地的逻辑
- 但按你确认后的业务,这部分不应视为当前核心业务链路
- 也就是说,代码现状和业务认知这里存在偏差,文档以你刚确认的业务为准
## 11. 按钮状态控制的当前真实逻辑
这是你提到“有点忘了”的重点之一。
当前 GPIO 按钮监控在 `gpio_monitor` 包里,监听:
- 按钮 GPIO`111`
- 蜂鸣器 GPIO`109`
### 11.1 按钮不是启动/停止按钮
当前业务里,按钮按下后不会:
- 启动推理
- 停止推理
- 直接触发报警
按钮的真实业务含义是:
- 临时关闭“人员离开”这一类报警的上报
- 但火焰检测报警仍然保持正常上报
### 11.2 为什么要有这个按钮
这是为了解决现场的特殊用工场景。
例如后厨煲汤:
- 厨师需要很早开始生火
- 但不可能在整个过程中始终守在灶台旁
- 现场管理允许短时间离开
- 如果仍严格套用“动火离人”规则,就会不断产生不符合现场管理实际的报警
所以按钮的业务意义是:
- 在允许短暂离岗的场景下,临时屏蔽人员离开报警
- 同时保留火焰检测能力,避免真正的火焰风险漏报
### 11.3 按钮按下后的目标行为
按你的描述,正确业务应该是:
- `fire_leave` 报警在暂停窗口内不再上报
- `fire_check` 报警仍然是每次检测到火焰后都正常上报
### 11.4 当前代码和目标业务的偏差
当前代码里,按钮实际上是在切换 `reporter` 里的事件周期状态:
- `fire_leave`
- `fire_check`
并带有:
- 200ms 去抖
- 5 秒冷却
但这和你描述的真实业务目标并不一致。
也就是说:
- 现在代码实现的是“事件周期切换”
- 你要的业务其实是“人员离开报警进入暂停期”
这部分应视为待修正逻辑。
## 12. 蜂鸣器逻辑
蜂鸣器不由本地报警自动触发,而是来自平台 topic:
- 订阅 `/dhlr/alert`
收到消息后,如果内容里的:
- `type == 1`
则触发本地蜂鸣器响指定秒数,默认 3 秒。
所以当前蜂鸣器逻辑是:
- 平台推送声光报警命令
- 微服务本地点响 GPIO109
不是“本地一检测到报警就必然自己响”。
## 13. `/dhlr/forbid_time` 的真实业务目标与当前问题
按你刚补充的业务,这个 topic 的核心作用不是“事件周期切换”,而是:
- 当前端在按钮按下后,下发一个“允许人员短暂离开”的暂停时长
- 微服务收到后,进入一个人员离开报警暂停窗口
- 在这个窗口内,即使检测到人员离开,也不要触发 `fire_leave` 上报
-`fire_check` 火焰报警仍然要照常上报
### 13.1 真实业务目标
`/dhlr/forbid_time` 应该承载的是:
- 暂停人员离开报警的时长
- 或与之等价的延迟/禁止上报控制
### 13.2 当前代码现状
当前代码收到 `/dhlr/forbid_time` 后,主要做的是:
1. 保存 `minute``/data/cache/forbid_time_config.json`
2. 保存 `fire_leave_timeout` / `fire_check_timeout``/data/cache/event_cycle_timeout.json`
3. 继续服务于 `reporter` 里的事件周期逻辑
### 13.3 当前存在的问题
这和你描述的真实业务不一致,问题点在于:
- 订阅逻辑没有真正控制 `fire_leave` 报警暂停窗口
- 没有把“按钮触发的允许离岗时长”落到主报警判定里
- 暂停期内是否跳过 `fire_leave` 上报,当前主流程没有真正接进去
- `fire_check``fire_leave` 的差异化处理没有按这个业务目标落地
### 13.4 应有的目标行为
这部分后续应改成:
1. 前端按钮按下
2. 后端通过 `/dhlr/forbid_time` 下发暂停时长
3. 微服务记录 `fire_leave` 报警暂停截止时间
4. 在暂停窗口内:
- 人员离开检测继续运行
- 但不触发 `fire_leave` 报警上报
5. 火焰检测逻辑不受影响,继续正常上报
## 14. 推理程序保活与异常恢复
代码里做了基础保活。
### 14.1 心跳来源
每次推理程序向本地 `/video/post` 发结果时,都会更新一次心跳时间。
也就是说:
- 心跳不是单独接口
- 心跳就是“推理结果持续回传”
### 14.2 超时判断
如果超过 `85` 秒没有收到某个推理程序的数据,就认为需要重启。
主循环每 `30` 秒检查一次:
- `fire_leave`
- `fire_check`
如果超时,则:
1. 杀掉旧进程
2. 复用原端口重启 `yolov5`
3. 立即刷新心跳
## 15. 当前代码里值得特别记住的几个事实
### 15.1 动火离人报警不是“火焰+离人”
当前代码里 `fire_leave` 报警依赖的是:
- 无人
- 温度超阈值
不是火焰检测结果。
### 15.2 火焰报警是完全独立的第二条链路
`fire_check` 只看火焰检测结果,单独触发 `fire_check` 级别事件。
### 15.3 两套检测区域是独立配置
- `FireLeaveDetectArea`
- `FireCheckDetectArea`
前端可以分别配置。
### 15.4 前端参数里真正参与当前判定的关键值
动火离人链路:
- `param.fire_leave.detection_time`
- `param.fire_leave.temperature_threshold`
- `param.fire_leave.alarm_time`
- 人数结果来自 `fire_leave` 模型
火焰链路:
- `param.fire_check.detection_time`
- 火焰结果来自 `fire_check` 模型
### 15.5 置信度当前是共用的
当前两个 `yolov5` 进程启动时都使用:
- `deviceData.Confidence`
没有看到两套模型分别使用不同置信度字段的实现。
## 16. 结合当前代码后的业务总结
如果用最贴近当前实现的话来概括:
- 前端负责配置设备、检测区和阈值
- 后端负责配置转发、消息汇总和给前端展示
- 微服务负责拉起两套推理和一套测温程序
- `fire_leave` 模型负责人数统计
- 测温程序负责温度
- 微服务把“无人 + 超温 + 持续时长”判定为动火离人报警
- `fire_check` 模型负责火焰检测
- 微服务把“持续检测到火焰”判定为火焰报警
- 两类报警都会回溯视频、上传视频、再通过 WebSocket 上报后端
## 17. 当前代码里可能需要你后续再确认的点
这几项不是我猜测,而是从代码现状看出来“可能存在设计与实现不完全一致”的地方:
1. `ConfigUpdateChan` 已有生产者,但未看到消费者
- 说明配置热更新链路可能没做完,或在当前仓库外
2. GPIO 按钮当前实现的是事件周期切换,但真实业务需要的是“暂停 fire_leave 上报”
- 说明这部分现在是实现偏差,不只是保留能力
3. `/dhlr/forbid_time` 当前没有真正接入 `fire_leave` 报警暂停控制
- 说明“按钮按下后允许短时离岗”的核心业务还没真正落地
4. `fire_leave``person_count` 参数没有直接用于报警判断
- 当前代码写死判断 `PersonCount == 0`
- 如果业务上原本支持“少于 N 人也报警”,那这部分目前没有实现
5. `param.fire_leave.confidence``param.fire_check.confidence`
- 配置结构里有
- 但实际启动命令仍统一使用 `deviceData.Confidence`
---
如果后面你继续描述前端和后端的预期业务,我可以再基于这份文档继续补一版“设计业务逻辑 vs 当前代码实现差异”文档,专门帮你找现在代码和你脑中业务之间的偏差。