feat: add dynamic config reload, unique device identity (UUID), and MQTT support
- New StreamManager for dynamic RTSP/FLV stream lifecycle - Dynamic worker scaling for inference - Device UUID generation and persistence - Telegraf config and NPU monitoring scripts - .gitignore for build artifacts
This commit is contained in:
123
internal/control/mqtt_manager.go
Normal file
123
internal/control/mqtt_manager.go
Normal file
@@ -0,0 +1,123 @@
|
||||
package control
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"log"
|
||||
"time"
|
||||
|
||||
mqtt "github.com/eclipse/paho.mqtt.golang"
|
||||
"tianyan-edge/internal/config"
|
||||
)
|
||||
|
||||
type MqttManager struct {
|
||||
client mqtt.Client
|
||||
cfg *config.Config
|
||||
updates chan *config.Config // Channel to broadcast config updates
|
||||
}
|
||||
|
||||
func NewMqttManager(cfg *config.Config) *MqttManager {
|
||||
return &MqttManager{
|
||||
cfg: cfg,
|
||||
updates: make(chan *config.Config, 10),
|
||||
}
|
||||
}
|
||||
|
||||
func (m *MqttManager) Run(ctx context.Context) {
|
||||
if m.cfg.MqttBroker == "" {
|
||||
log.Println("mqtt: no broker configured, skipping")
|
||||
return
|
||||
}
|
||||
|
||||
opts := mqtt.NewClientOptions()
|
||||
opts.AddBroker(m.cfg.MqttBroker)
|
||||
opts.SetClientID(m.cfg.GetDeviceIdentity())
|
||||
opts.SetUsername(m.cfg.MqttUser)
|
||||
opts.SetPassword(m.cfg.MqttPass)
|
||||
opts.SetAutoReconnect(true)
|
||||
opts.SetMaxReconnectInterval(1 * time.Minute)
|
||||
|
||||
// LWT
|
||||
willTopic := fmt.Sprintf("tianyan/edge/%s/status", m.cfg.GetDeviceIdentity())
|
||||
opts.SetWill(willTopic, "offline", 1, true)
|
||||
|
||||
opts.SetOnConnectHandler(func(c mqtt.Client) {
|
||||
log.Println("mqtt: connected to broker")
|
||||
c.Publish(willTopic, 1, true, "online")
|
||||
configTopic := fmt.Sprintf("tianyan/edge/%s/config", m.cfg.GetDeviceIdentity())
|
||||
token := c.Subscribe(configTopic, 1, m.handleConfigUpdate)
|
||||
token.Wait()
|
||||
if token.Error() != nil {
|
||||
log.Printf("mqtt: subscribe failed: %v", token.Error())
|
||||
} else {
|
||||
log.Printf("mqtt: subscribed to %s", configTopic)
|
||||
}
|
||||
})
|
||||
|
||||
m.client = mqtt.NewClient(opts)
|
||||
if token := m.client.Connect(); token.Wait() && token.Error() != nil {
|
||||
log.Printf("mqtt: connect error: %v", token.Error())
|
||||
}
|
||||
|
||||
<-ctx.Done()
|
||||
m.client.Disconnect(1000)
|
||||
log.Println("mqtt: disconnected")
|
||||
}
|
||||
|
||||
func (m *MqttManager) handleConfigUpdate(c mqtt.Client, msg mqtt.Message) {
|
||||
log.Printf("mqtt: received config update on %s", msg.Topic())
|
||||
var newCfg map[string]interface{}
|
||||
if err := json.Unmarshal(msg.Payload(), &newCfg); err != nil {
|
||||
log.Printf("mqtt: config parse error: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
// Update local config struct
|
||||
if v, ok := newCfg["infer_fps"].(float64); ok {
|
||||
m.cfg.InferFPS = int(v)
|
||||
}
|
||||
if v, ok := newCfg["conf_threshold"].(float64); ok {
|
||||
m.cfg.ConfThreshold = v
|
||||
}
|
||||
if v, ok := newCfg["infer_workers"].(float64); ok {
|
||||
m.cfg.InferWorkers = int(v)
|
||||
}
|
||||
if v, ok := newCfg["rtsp_urls"]; ok {
|
||||
if urls, ok := v.([]interface{}); ok {
|
||||
var s []string
|
||||
for _, u := range urls {
|
||||
if str, ok := u.(string); ok {
|
||||
s = append(s, str)
|
||||
}
|
||||
}
|
||||
m.cfg.RTSPURLs = s
|
||||
}
|
||||
}
|
||||
|
||||
// Save to disk
|
||||
if err := m.cfg.Save(); err != nil {
|
||||
log.Printf("mqtt: failed to save config: %v", err)
|
||||
} else {
|
||||
log.Println("mqtt: config saved to disk")
|
||||
}
|
||||
|
||||
// Broadcast update to other components
|
||||
select {
|
||||
case m.updates <- m.cfg:
|
||||
default:
|
||||
log.Println("mqtt: update channel full, dropping update")
|
||||
}
|
||||
}
|
||||
|
||||
func (m *MqttManager) GetUpdates() <-chan *config.Config {
|
||||
return m.updates
|
||||
}
|
||||
|
||||
func (m *MqttManager) Publish(topic string, payload interface{}) {
|
||||
if m.client == nil || !m.client.IsConnected() {
|
||||
return
|
||||
}
|
||||
data, _ := json.Marshal(payload)
|
||||
m.client.Publish(topic, 1, false, data)
|
||||
}
|
||||
Reference in New Issue
Block a user