feat: update inference reporting and ffmpeg video merge

This commit is contained in:
gqc
2026-07-06 16:26:08 +08:00
parent 79490bd27b
commit 0842f555fd
10 changed files with 1430 additions and 990 deletions
@@ -0,0 +1,66 @@
# 2026-07-05 22:40 Global Status Log Fix
## Problem
In fire_check-only mode, the global status monitor still printed values like:
```text
person:1 | temp:0.0°C | threshold value:70.0°C
```
Those fields belong to fire_leave/thermometry logic. When thermometry startup fails and fire_leave is disabled, these values are not active runtime measurements and should not be shown as real current data.
## Change
- Added latest fire_check result state in `main.go`:
- `FireCheckHasResult`
- `FireCheckLastNumber`
- Updated the fire_check result branch to record the latest detected fire target count.
- Updated `printGlobalDeviceStatus()`:
- prints `fire:<value>` for fire_check target count;
- prints `fire:null` before any fire_check result is received;
- prints `person:null`, `temp:null`, and `threshold value:null` when fire_leave is disabled;
- keeps `person/temp/threshold` only when fire_leave is active;
- adds `fire_candidate`, `fire_last_seen_age`, and `fire_leave_enabled`.
## Expected Logs
Before any fire_check inference result is received:
```text
[1] DeviceUUID=xxx | fire:null | fire_candidate:false | fire_last_seen_age:null | fire_leave_enabled:false | person:null | temp:null | threshold value:null | cooling time:0s
```
After fire_check reports one fire target:
```text
[1] DeviceUUID=xxx | fire:1 | fire_candidate:true | fire_last_seen_age:500ms | fire_leave_enabled:false | person:null | temp:null | threshold value:null | cooling time:0s
```
If fire_check has reported a result but no fire target is present:
```text
[1] DeviceUUID=xxx | fire:0 | fire_candidate:false | fire_last_seen_age:null | fire_leave_enabled:false | person:null | temp:null | threshold value:null | cooling time:0s
```
## Impact
- Does not change fire_check alarm upload/report logic.
- Does not change heartbeat JSON format.
- Does not change inference startup arguments.
- Only changes runtime state tracking and global monitor log output.
- fire_leave logs remain available when fire_leave and thermometry are active.
## Verification
```text
gofmt -w main.go
go test ./...
go build -o bin/fireleave_tool.new .
```
The new binary was installed as:
```text
bin/fireleave_tool
```
+241
View File
@@ -0,0 +1,241 @@
# 2026-07-05 修改日志
本文记录本次代码审查后实际落地的修复、影响范围、验证结果和构建产物。
## 1. 本次目标
修复代码审查中确认的 1、2、4、5、8 号问题,并重新构建新的程序到 `bin/fireleave_tool`
涉及的问题包括:
- `fire_check` 报警上传/上报失败时仍被当作成功处理
- 心跳超时重启会扩大影响,可能同时重启两个推理进程
- 视频时间戳解析和合并异常处理不可靠
- WebSocket 重连可能并发启动多个重连循环
- `copyFile` 超时返回后后台 goroutine 仍可能继续写文件
## 2. 修改文件
主要修改:
- `main.go`
- `connect/heartbeat.go`
- `connect/ws_channel.go`
- `reporter/reporter.go`
- `video_server/video_merge.go`
- `bin/fireleave_tool`
构建/环境相关:
- 本机安装了 Go 编译器
- 当前构建使用 `go1.26.4`
- 新二进制目标为 `linux/arm64`
## 3. 详细修改内容
### 3.1 `fire_check` 上报成功语义修复
原问题:
`fire_check` 报警触发后,上传视频和 WebSocket 上报在异步流程中执行。调用方无法知道最终是否真正上传成功、是否真正写入 WebSocket,因此主流程可能提前清理 `FireCheckCandidate` 状态。这样会造成“报警没有报上去,但本地状态认为已经处理完成”的风险。
修改后:
- `reporter.UploadVideoAndReportFireCheck()` 改为同步执行。
- 视频上传失败会返回 error。
- WebSocket 未初始化或未连接会返回 error。
- 事件上报失败会返回 error。
- 数据快照上报失败会返回 error。
- 只有视频上传、事件上报、快照上报都成功后才清理合并文件并返回成功。
- `reporter.TriggerFireCheckAlarm()` 改为返回 `bool`
- `true` 表示报警已完整处理成功
- `false` 表示合并、上传、上报或并发锁获取失败
- `main.go` 中只有当 `TriggerFireCheckAlarm()` 返回 `true` 时才清空 `FireCheckCandidate``FireCheckFirstSeen``FireCheckLastSeen`
影响:
- `fire_check` 报警处理会比原来更严格,调用路径会等待上传和 WebSocket 写入完成。
- 如果平台或网络异常,本地候选状态不会被误清理,后续仍有机会重新触发。
- 失败场景下日志可能增加,但这是为了避免报警丢失。
### 3.2 `fire_check` 连续检测状态修复
原问题:
旧逻辑在收到推理结果的分支中立即判断持续时间并触发上报。如果上报失败,状态仍可能被重置。同时检测结果和上报处理耦合较重。
修改后:
- `deviceAlarmState` 中使用:
- `FireCheckCandidate`
- `FireCheckFirstSeen`
- `FireCheckLastSeen`
- 收到火焰推理结果时只更新候选状态和最近一次看到火焰的时间。
-`monitorTicker` 分支中统一判断是否达到 `fire_check` 持续时间。
- 增加 `fireCheckLastSeenGrace`,避免检测帧之间的短暂间隔导致候选状态过早失效。
影响:
- 火焰检测持续时间判断更稳定。
- 上报是否成功会影响候选状态是否清理。
- 短暂丢帧不一定立即打断候选状态。
### 3.3 心跳重启范围修复
原问题:
`fire_leave``fire_check` 某一个推理进程心跳超时时,旧逻辑会调用共享重启流程,可能把两个推理进程都重启。这样会扩大影响面,也可能造成重复重启。
修改后:
- 新增 `connect.RestartInferenceProcess(deviceData, processUUID)`
- 该函数只重启指定的 `processUUID`
- 主模型:`deviceUUID`
- 火焰模型:`deviceUUID_fire_check`
- `connect.StartInferenceProcess()` 保留兼容行为,仍可重启两路推理。
- `main.go` 心跳监控改为:
- `fire_leave` 超时只重启 `deviceUUID`
- `fire_check` 超时只重启 `deviceUUID_fire_check`
- 重启后只刷新对应进程的心跳时间。
影响:
- 单路推理异常不会连带影响另一路推理。
- 保留旧的 `StartInferenceProcess()`,避免已有调用点失效。
- 重启逻辑仍使用 `connect.GlobalPortManager` 获取原端口。
### 3.4 视频合并异常处理修复
原问题:
`video_server.parseSimpleTimestamp()` 直接按固定下标切片,遇到异常文件名可能 panic。`MergeVideo()` 虽然有 recover,但旧逻辑存在 panic 后没有明确错误返回的风险。
修改后:
- `MergeVideo()` 改为命名返回值:
- `mergedPath string`
- `err error`
- `recover` 捕获 panic 后会设置:
- `mergedPath = ""`
- `err = fmt.Errorf("merge video panic: %v", recovered)`
- `parseSimpleTimestamp()` 增加文件名长度校验。
- 年、月、日、时、分、秒解析失败时都会返回明确 error。
- `getTimestampFromPath()` 改为返回 `(int64, error)`
- 排序和时间戳匹配逻辑会处理解析失败,必要时回退到文件名排序。
影响:
- 异常文件名不会导致程序崩溃。
- 视频合并失败会更明确地返回错误,调用方可感知失败。
- 正常命名的 mp4 文件处理逻辑不变。
### 3.5 WebSocket 重连并发修复
原问题:
读循环、心跳循环、发送失败都可能触发 `reconnect()`。如果多个错误同时发生,可能启动多个重连 goroutine,并在重连成功后重复启动心跳循环。
修改后:
- `WSClient` 增加 `reconnecting bool`
- 新增:
- `startReconnect()`
- `startReconnectLocked()`
- 所有触发重连的地方都通过 `startReconnect` 系列方法进入。
- 同一时间只允许一个重连循环运行。
- 重连完成后重置 `reconnecting`
- 重连成功后只恢复 `readLoop()`,不再重复启动新的 `heartbeatLoop()`
- 重新订阅时先复制 `subscribers` map,再遍历订阅,降低并发读写风险。
- `RegisterService()``SubscribeTopic()` 使用本地 `client := GetWSClient()`,减少直接访问全局变量的竞态窗口。
影响:
- 断线时重连行为更可控。
- 减少 goroutine 堆积和重复 heartbeat 的风险。
- WebSocket 正常收发路径不变。
### 3.6 `copyFile` 超时写入修复
原问题:
`copyFile` 使用 goroutine 执行复制,外层通过 context 超时返回。超时后后台 goroutine 仍可能继续写目标文件,导致函数已经失败但文件仍在被写入,甚至留下半成品。
修改后:
- `copyFile` 改为同步分块复制。
- 每次循环检查 30 秒 deadline。
- 读取和写入使用 1MB buffer。
- 超时、读失败、写失败、短写或 sync 失败时都会删除目标文件。
- 函数返回后不再有后台 goroutine 继续写文件。
影响:
- 超时后不会继续写目标文件。
- 失败时不保留不完整目标文件。
- 大文件复制仍可能耗时,但行为更确定。
## 4. 构建与验证
已执行:
```text
gofmt -w main.go connect/heartbeat.go connect/ws_channel.go reporter/reporter.go video_server/video_merge.go
go test ./...
go build -o bin/fireleave_tool.new .
```
验证结果:
```text
go test ./... 通过
所有包均为 [no test files]
```
新构建产物:
```text
bin/fireleave_tool
```
构建信息:
```text
go version: go1.26.4
GOOS: linux
GOARCH: arm64
CGO_ENABLED: 0
binary size: 10011779 bytes
```
构建完成后已清理 `bin` 目录旧产物,当前 `bin` 目录只保留:
```text
bin/fireleave_tool
```
## 5. 兼容性与风险说明
### 保持兼容的部分
- `connect.StartInferenceProcess()` 保留,已有调用仍可使用。
- `fire_leave` 报警入口仍保持异步上报行为。
- WebSocket 消息结构和 topic 订阅逻辑没有改协议。
- 视频文件命名规则未改变。
### 行为变化
- `fire_check` 上报变为同步确认成功。
- `fire_check` 上报失败时不会清空候选报警状态。
- 心跳超时只重启异常的单个推理进程。
- WebSocket 同一时间只允许一个重连流程。
- 文件复制超时会删除不完整目标文件。
### 仍需关注
- 当前部分历史中文注释已经存在编码损坏,后续维护时建议逐步替换成清晰 UTF-8 注释。
- `reporter` 中仍有旧的 event cycle 相关逻辑,当前未完全重构,只做了本次问题相关的收敛。
- 如果现场网络长期异常,`fire_check` 候选状态可能持续重试,需要结合日志确认是否需要增加队列、退避或最大重试策略。
## 6. 本次结论
本次修复重点是避免误判成功、缩小重启影响、减少并发重连和后台写文件风险。新的 `bin/fireleave_tool` 已基于修复后的代码构建完成,可用于后续部署验证。