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:
2026-05-08 16:26:10 +08:00
parent 2e7245d62e
commit 6e88d2392f
12 changed files with 433 additions and 70 deletions

View File

@@ -46,31 +46,75 @@ type dedupState struct {
}
type Client struct {
cfg *config.Config
frames <-chan stream.Frame
events chan<- event.SuspectedEvent
dedup *dedupState
cfg *config.Config
frames <-chan stream.Frame
events chan<- event.SuspectedEvent
dedup *dedupState
updates <-chan *config.Config
}
func NewClient(cfg *config.Config, frames <-chan stream.Frame, events chan<- event.SuspectedEvent) *Client {
func NewClient(cfg *config.Config, frames <-chan stream.Frame, events chan<- event.SuspectedEvent, updates <-chan *config.Config) *Client {
return &Client{
cfg: cfg,
frames: frames,
events: events,
dedup: &dedupState{seen: make(map[string]float64)},
cfg: cfg,
frames: frames,
events: events,
dedup: &dedupState{seen: make(map[string]float64)},
updates: updates,
}
}
func (c *Client) Run(ctx context.Context) {
var wg sync.WaitGroup
for i := 0; i < c.cfg.InferWorkers; i++ {
// Worker management loop
numWorkers := c.cfg.InferWorkers
activeWorkers := make([]context.CancelFunc, numWorkers)
for i := 0; i < numWorkers; i++ {
workerCtx, cancel := context.WithCancel(ctx)
activeWorkers[i] = cancel
wg.Add(1)
go func(workerID int) {
go func(id int, wCtx context.Context) {
defer wg.Done()
c.workerLoop(ctx, workerID)
}(i)
c.workerLoop(wCtx, id)
}(i, workerCtx)
}
for {
select {
case <-ctx.Done():
for _, cancel := range activeWorkers {
cancel()
}
wg.Wait()
return
case newCfg := <-c.updates:
c.cfg = newCfg
target := c.cfg.InferWorkers
if target < 0 {
target = 0
}
// Scale up
for i := len(activeWorkers); i < target; i++ {
workerCtx, cancel := context.WithCancel(ctx)
activeWorkers = append(activeWorkers, cancel)
wg.Add(1)
go func(id int, wCtx context.Context) {
defer wg.Done()
c.workerLoop(wCtx, id)
}(i, workerCtx)
}
// Scale down
if target < len(activeWorkers) {
for i := target; i < len(activeWorkers); i++ {
activeWorkers[i]()
}
activeWorkers = activeWorkers[:target]
}
log.Printf("infer: workers scaled to %d", target)
}
}
wg.Wait()
}
func (c *Client) workerLoop(ctx context.Context, workerID int) {
@@ -89,10 +133,9 @@ func (c *Client) workerLoop(ctx context.Context, workerID int) {
log.Printf("infer[w%d]: socket connected", workerID)
if !c.loop(ctx, conn, workerID) {
conn.Close()
return
time.Sleep(1 * time.Second)
}
conn.Close()
time.Sleep(500 * time.Millisecond)
}
}
@@ -103,12 +146,10 @@ func (c *Client) loop(ctx context.Context, conn net.Conn, workerID int) bool {
return false
case f := <-c.frames:
if err := c.send(conn, f); err != nil {
log.Printf("infer[w%d]: send error: %v", workerID, err)
return true
}
res, err := c.recv(conn)
if err != nil {
log.Printf("infer[w%d]: recv error: %v", workerID, err)
return true
}
c.emitEvents(f, res)