package device import ( "fmt" "log" "sync" "visiona-local/server/internal/driver" "visiona-local/server/internal/driver/kneron" "visiona-local/server/pkg/logger" ) type Manager struct { registry *DriverRegistry sessions map[string]*DeviceSession eventBus chan DeviceEvent scriptPath string logBroadcaster *logger.Broadcaster mu sync.RWMutex } func NewManager(registry *DriverRegistry, scriptPath string) *Manager { return &Manager{ registry: registry, sessions: make(map[string]*DeviceSession), eventBus: make(chan DeviceEvent, 100), scriptPath: scriptPath, } } // SetLogBroadcaster attaches a log broadcaster so that Kneron driver // and bridge logs are forwarded to the frontend. func (m *Manager) SetLogBroadcaster(b *logger.Broadcaster) { m.logBroadcaster = b // Also set on any already-registered kneron drivers. m.mu.RLock() defer m.mu.RUnlock() for _, s := range m.sessions { if kd, ok := s.Driver.(*kneron.KneronDriver); ok { kd.SetLogBroadcaster(b) } } } func (m *Manager) Start() { // Detect real Kneron devices (KL520, KL720, etc.) via Python bridge. devices := kneron.DetectDevices(m.scriptPath) if len(devices) == 0 { log.Println("No Kneron devices detected") return } m.mu.Lock() defer m.mu.Unlock() for _, info := range devices { d := kneron.NewKneronDriver(info, m.scriptPath) if m.logBroadcaster != nil { d.SetLogBroadcaster(m.logBroadcaster) } m.sessions[info.ID] = NewSession(d) log.Printf("Registered Kneron device: %s (%s, type=%s)", info.Name, info.ID, info.Type) } } // Rescan re-detects connected Kneron devices. New devices are registered, // removed devices are cleaned up, and existing devices are left untouched. func (m *Manager) Rescan() []driver.DeviceInfo { detected := kneron.DetectDevices(m.scriptPath) // Build a set of detected device IDs. detectedIDs := make(map[string]driver.DeviceInfo, len(detected)) for _, info := range detected { detectedIDs[info.ID] = info } m.mu.Lock() defer m.mu.Unlock() // Remove devices that are no longer present. for id, s := range m.sessions { if _, exists := detectedIDs[id]; !exists { log.Printf("Device removed: %s", id) s.Driver.Disconnect() delete(m.sessions, id) } } // Add newly detected devices. for _, info := range detected { if _, exists := m.sessions[info.ID]; !exists { d := kneron.NewKneronDriver(info, m.scriptPath) if m.logBroadcaster != nil { d.SetLogBroadcaster(m.logBroadcaster) } m.sessions[info.ID] = NewSession(d) log.Printf("Registered Kneron device: %s (%s, type=%s)", info.Name, info.ID, info.Type) } } // Return current list. devices := make([]driver.DeviceInfo, 0, len(m.sessions)) for _, s := range m.sessions { devices = append(devices, s.Driver.Info()) } return devices } func (m *Manager) ListDevices() []driver.DeviceInfo { m.mu.RLock() defer m.mu.RUnlock() devices := make([]driver.DeviceInfo, 0, len(m.sessions)) for _, s := range m.sessions { devices = append(devices, s.Driver.Info()) } return devices } func (m *Manager) GetDevice(id string) (*DeviceSession, error) { m.mu.RLock() defer m.mu.RUnlock() s, ok := m.sessions[id] if !ok { return nil, fmt.Errorf("device not found: %s", id) } return s, nil } func (m *Manager) Connect(id string) error { s, err := m.GetDevice(id) if err != nil { return err } if err := s.Driver.Connect(); err != nil { return err } m.eventBus <- DeviceEvent{Event: "updated", Device: s.Driver.Info()} return nil } func (m *Manager) Disconnect(id string) error { s, err := m.GetDevice(id) if err != nil { return err } if err := s.Driver.Disconnect(); err != nil { return err } m.eventBus <- DeviceEvent{Event: "updated", Device: s.Driver.Info()} return nil } func (m *Manager) Events() <-chan DeviceEvent { return m.eventBus }