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

@@ -0,0 +1,96 @@
package control
import (
"testing"
)
func TestParseNpuSmiOutput(t *testing.T) {
common := `Temperature(C) : 46
Aicore Usage Rate(%) : 25
Memory Usage Rate(%) : 86`
memory := `Capacity(MB) : 3513`
stats := parseNpuSmiOutput(common, memory)
if stats == nil {
t.Fatal("expected non-nil stats")
}
if stats.TempC != 46 {
t.Errorf("temp: got %d want 46", stats.TempC)
}
if stats.UtilPct != 25 {
t.Errorf("util: got %d want 25", stats.UtilPct)
}
if stats.MemTotalMB != 3513 {
t.Errorf("mem_total: got %d want 3513", stats.MemTotalMB)
}
if stats.MemPct != 86 {
t.Errorf("mem_pct: got %d want 86", stats.MemPct)
}
// 3513 * 86 / 100 = 3021
if stats.MemUsedMB != 3021 {
t.Errorf("mem_used: got %d want 3021", stats.MemUsedMB)
}
}
func TestParseInfluxLine(t *testing.T) {
line := "npu_status,device=0 temp=46,utilization=0,memory_used=3021,memory_total=3513"
stats := parseInfluxLine(line)
if stats == nil {
t.Fatal("expected non-nil stats")
}
if stats.TempC != 46 {
t.Errorf("temp: got %d want 46", stats.TempC)
}
if stats.UtilPct != 0 {
t.Errorf("util: got %d want 0", stats.UtilPct)
}
if stats.MemUsedMB != 3021 {
t.Errorf("mem_used: got %d want 3021", stats.MemUsedMB)
}
if stats.MemTotalMB != 3513 {
t.Errorf("mem_total: got %d want 3513", stats.MemTotalMB)
}
}
func TestParseInfluxLineInvalid(t *testing.T) {
// Input with no space-separated fields returns nil
stats := parseInfluxLine("garbage")
if stats != nil {
t.Error("expected nil stats for garbage input without fields")
}
// Input with fields but no valid data returns zero-valued stats
stats2 := parseInfluxLine("npu_status,device=0 ")
if stats2 == nil {
t.Fatal("expected non-nil stats for input with fields")
}
if stats2.TempC != 0 || stats2.UtilPct != 0 {
t.Errorf("expected zero values, got temp=%d util=%d", stats2.TempC, stats2.UtilPct)
}
}
func TestExtractLastNumber(t *testing.T) {
tests := []struct {
input string
want int
}{
{"Temperature(C) : 46", 46},
{"Aicore Usage Rate(%) : 25%", 25},
{" 86%", 86},
{"no numbers here", 0},
{"", 0},
{"value: 123C", 123},
}
for _, tt := range tests {
got := extractLastNumber(tt.input)
if got != tt.want {
t.Errorf("extractLastNumber(%q) = %d, want %d", tt.input, got, tt.want)
}
}
}
func TestInMaintenanceWindow(t *testing.T) {
// Can't test specific hours without mocking time.Time,
// but verify it doesn't panic
_ = inMaintenanceWindow()
}