# 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` 已基于修复后的代码构建完成,可用于后续部署验证。