fix: 修复6个边缘侧问题 + 单元测试

1. 删除 ConfigAgent (与 MQTT 配置更新重叠且未启用)
2. OTA 完整安装流程: 解压tar.gz -> 校验ELF -> 替换二进制 -> systemctl重启
3. StreamManager stopAll 添加 Wait() 防止僵尸进程
4. InferClient socket 读写添加 timeout 防止永久阻塞
5. Heartbeat 集成 NPU 监控 (npu-smi + fallback 脚本)
6. 配置更新时动态重启 ffmpeg 以应用新 fps
This commit is contained in:
2026-05-08 23:58:08 +08:00
parent 88b2e71a77
commit 97a77d4745
11 changed files with 843 additions and 186 deletions

View File

@@ -208,10 +208,17 @@ func (c *Client) send(conn net.Conn, f stream.Frame) error {
if err != nil {
return err
}
conn.SetWriteDeadline(time.Now().Add(10 * time.Second))
defer conn.SetWriteDeadline(time.Time{})
return writeMsg(conn, data)
}
func (c *Client) recv(conn net.Conn) (*resultMsg, error) {
// Set read deadline to prevent blocking forever if inference server hangs
// 30s timeout covers even slow NPU inference on large images
conn.SetReadDeadline(time.Now().Add(30 * time.Second))
defer conn.SetReadDeadline(time.Time{}) // reset after read
data, err := readMsg(conn)
if err != nil {
return nil, err

View File

@@ -0,0 +1,80 @@
package infer
import (
"testing"
"tianyan-edge/internal/config"
)
func TestIsDuplicate(t *testing.T) {
cfg := &config.Config{DedupWindowSec: 30}
c := &Client{
cfg: cfg,
dedup: &dedupState{seen: make(map[string]float64)},
}
// First detection should NOT be duplicate (uses default config dedup window of 30s)
// ts=100.0, key="cam-001|person" — first time, not duplicate
if c.isDuplicate("cam-001", "person", 100.0) {
t.Error("first detection should not be duplicate")
}
// Same device+class within window should BE duplicate
// ts=105.0, 105-100=5 < 30, duplicate
if !c.isDuplicate("cam-001", "person", 105.0) {
t.Error("same event within window should be duplicate")
}
// Same device+class outside window should NOT be duplicate
// ts=200.0, 200-105=95 > 30, not duplicate (updates the timestamp)
if c.isDuplicate("cam-001", "person", 200.0) {
t.Error("same event outside window should not be duplicate")
}
// Different class should NOT be duplicate
if c.isDuplicate("cam-001", "car", 105.0) {
t.Error("different class should not be duplicate")
}
// Different device should NOT be duplicate
if c.isDuplicate("cam-002", "person", 105.0) {
t.Error("different device should not be duplicate")
}
}
func TestDedupMapCleanup(t *testing.T) {
cfg := &config.Config{DedupWindowSec: 30}
c := &Client{
cfg: cfg,
dedup: &dedupState{seen: make(map[string]float64)},
}
// Fill up the map with old entries
for i := 0; i < 5001; i++ {
c.dedup.seen["cam-001|person"] = float64(i * 10)
}
// Trigger cleanup by adding a new entry
c.isDuplicate("cam-001", "person", 99999.0)
if len(c.dedup.seen) > 5000 {
t.Errorf("dedup map should be cleaned up, got %d entries", len(c.dedup.seen))
}
}
func TestMustUUID(t *testing.T) {
u1 := mustUUID()
u2 := mustUUID()
if u1 == u2 {
t.Error("UUIDs should be unique")
}
// Check format: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx (32 hex chars with dashes)
parts := 0
for _, c := range u1 {
if c == '-' {
parts++
}
}
if parts != 4 {
t.Errorf("UUID format: expected 4 dashes, got %d", parts)
}
}