feat: client config from yaml with interval/reconnect settings

This commit is contained in:
ngfchl
2026-06-19 16:46:41 +08:00
parent 22fb7e2c84
commit 742928a93d
2 changed files with 78 additions and 23 deletions
+13
View File
@@ -0,0 +1,13 @@
# Hardware Monitor Client 配置
#
# machine_name : 设备名称 (默认使用主机名)
# server : 服务端地址 (ip:port)
# interval : 采集间隔 (秒, 默认 3)
# reconnect_delay : 断线重连间隔 (秒, 默认 3)
#
# 删除此文件可重新配置
machine_name: my-pc
server: 192.168.1.100:8080
interval: 3
reconnect_delay: 3
+65 -23
View File
@@ -33,34 +33,56 @@ var (
// ═══════════════════════════════════════════════ // ═══════════════════════════════════════════════
type ClientConfig struct { type ClientConfig struct {
MachineName string `yaml:"machine_name"` MachineName string `yaml:"machine_name"`
Server string `yaml:"server"` Server string `yaml:"server"`
Interval int `yaml:"interval"`
ReconnectDelay int `yaml:"reconnect_delay"`
}
func defaultConfig() ClientConfig {
return ClientConfig{
Interval: 3,
ReconnectDelay: 3,
}
} }
func configPath() string { func configPath() string {
exe, err := os.Executable() // 优先: 当前工作目录
if err != nil { if wd, err := os.Getwd(); err == nil {
return "client.yaml" p := filepath.Join(wd, "client.yaml")
if _, err := os.Stat(p); err == nil {
return p
}
} }
return filepath.Join(filepath.Dir(exe), "client.yaml") // 其次: 可执行文件目录
if exe, err := os.Executable(); err == nil {
return filepath.Join(filepath.Dir(exe), "client.yaml")
}
return "client.yaml"
} }
func loadConfig() (ClientConfig, bool) { func loadConfig() (ClientConfig, bool) {
p := configPath() p := configPath()
raw, err := os.ReadFile(p) raw, err := os.ReadFile(p)
if err != nil { if err != nil {
return ClientConfig{}, false return defaultConfig(), false
} }
var c ClientConfig cfg := defaultConfig()
if yaml.Unmarshal(raw, &c) != nil { if yaml.Unmarshal(raw, &cfg) != nil {
return ClientConfig{}, false return defaultConfig(), false
} }
c.MachineName = strings.TrimSpace(c.MachineName) cfg.MachineName = strings.TrimSpace(cfg.MachineName)
c.Server = strings.TrimSpace(c.Server) cfg.Server = strings.TrimSpace(cfg.Server)
if c.MachineName == "" || c.Server == "" { if cfg.MachineName == "" || cfg.Server == "" {
return ClientConfig{}, false return defaultConfig(), false
} }
return c, true if cfg.Interval <= 0 {
cfg.Interval = 3
}
if cfg.ReconnectDelay <= 0 {
cfg.ReconnectDelay = 3
}
return cfg, true
} }
func saveConfig(c ClientConfig) error { func saveConfig(c ClientConfig) error {
@@ -68,7 +90,14 @@ func saveConfig(c ClientConfig) error {
if err != nil { if err != nil {
return err return err
} }
hdr := "# Monitor Client 配置\n# 删除此文件可重新输入设备信息\n\n" hdr := "# Hardware Monitor Client 配置\n"
hdr += "#\n"
hdr += "# machine_name : 设备名称 (默认使用主机名)\n"
hdr += "# server : 服务端地址 (ip:port)\n"
hdr += "# interval : 采集间隔 (秒, 默认 3)\n"
hdr += "# reconnect_delay : 断线重连间隔 (秒, 默认 3)\n"
hdr += "#\n"
hdr += "# 删除此文件可重新配置\n\n"
p := configPath() p := configPath()
os.MkdirAll(filepath.Dir(p), 0755) os.MkdirAll(filepath.Dir(p), 0755)
return os.WriteFile(p, append([]byte(hdr), out...), 0644) return os.WriteFile(p, append([]byte(hdr), out...), 0644)
@@ -414,7 +443,10 @@ func (m model) handleEnter() (tea.Model, tea.Cmd) {
addr += ":8080" addr += ":8080"
} }
m.cfg = ClientConfig{MachineName: name, Server: addr} cfg := defaultConfig()
cfg.MachineName = name
cfg.Server = addr
m.cfg = cfg
saveConfig(m.cfg) saveConfig(m.cfg)
// 切换到监控阶段 // 切换到监控阶段
@@ -869,6 +901,15 @@ func runCollector(done <-chan struct{}, cfg ClientConfig, dataCh chan<- MachineD
// 预热 // 预热
cpu.Percent(500*time.Millisecond, true) cpu.Percent(500*time.Millisecond, true)
interval := time.Duration(cfg.Interval) * time.Second
if interval < time.Second {
interval = 3 * time.Second
}
reconnect := time.Duration(cfg.ReconnectDelay) * time.Second
if reconnect < time.Second {
reconnect = 3 * time.Second
}
for { for {
select { select {
case <-done: case <-done:
@@ -877,7 +918,7 @@ func runCollector(done <-chan struct{}, cfg ClientConfig, dataCh chan<- MachineD
} }
wsURL := "ws://" + cfg.Server + "/ws/agent" wsURL := "ws://" + cfg.Server + "/ws/agent"
err := collectLoop(done, wsURL, cfg.MachineName, dataCh, statCh) err := collectLoop(done, wsURL, cfg.MachineName, interval, dataCh, statCh)
if err != nil { if err != nil {
select { select {
case statCh <- false: case statCh <- false:
@@ -889,12 +930,12 @@ func runCollector(done <-chan struct{}, cfg ClientConfig, dataCh chan<- MachineD
select { select {
case <-done: case <-done:
return return
case <-time.After(3 * time.Second): case <-time.After(reconnect):
} }
} }
} }
func collectLoop(done <-chan struct{}, wsURL, name string, dataCh chan<- MachineData, statCh chan<- bool) error { func collectLoop(done <-chan struct{}, wsURL, name string, interval time.Duration, dataCh chan<- MachineData, statCh chan<- bool) error {
conn, _, err := websocket.DefaultDialer.Dial(wsURL, nil) conn, _, err := websocket.DefaultDialer.Dial(wsURL, nil)
if err != nil { if err != nil {
return err return err
@@ -913,8 +954,8 @@ func collectLoop(done <-chan struct{}, wsURL, name string, dataCh chan<- Machine
default: default:
} }
// 采集 CPU 使用率 (阻塞 ~3s) // 采集 CPU 使用率
perCore, _ := cpu.Percent(3*time.Second, true) perCore, _ := cpu.Percent(interval, true)
data := collectAll(name, perCore) data := collectAll(name, perCore)
@@ -1130,9 +1171,10 @@ func main() {
cfg, loaded := loadConfig() cfg, loaded := loadConfig()
if loaded { if loaded {
fmt.Printf(" [✓] 加载配置: %s\n", configPath()) fmt.Printf(" [✓] 配置文件: %s\n", configPath())
fmt.Printf(" [✓] 设备名称: %s\n", cfg.MachineName) fmt.Printf(" [✓] 设备名称: %s\n", cfg.MachineName)
fmt.Printf(" [✓] 服务端 : %s\n", cfg.Server) fmt.Printf(" [✓] 服务端 : %s\n", cfg.Server)
fmt.Printf(" [✓] 采集间隔: %ds\n", cfg.Interval)
fmt.Println() fmt.Println()
} }