|
|
package scene
import ( "context" "git.aiterp.net/lucifer/new-server/app/config" "git.aiterp.net/lucifer/new-server/models" "log" "sync" "time" )
type Manager struct { mu sync.Mutex allocations map[int]*Scene devices map[int]*models.Device scenes []*Scene
notifyCh chan struct{} }
func (mgr *Manager) UpdateDevice(ctx context.Context, device *models.Device, data *models.Scene) error { mgr.mu.Lock() defer mgr.mu.Unlock()
scene, err := mgr.findScene(ctx, device.SceneAssignments, data) if err != nil { return err }
if mgr.allocations[device.ID] != nil && mgr.allocations[device.ID] != scene { mgr.allocations[device.ID].RemoveDevice(*device) }
if mgr.allocations[device.ID] != scene { if mgr.allocations == nil { mgr.allocations = make(map[int]*Scene, 8) }
mgr.allocations[device.ID] = scene
if scene != nil { scene.due = true } }
if scene != nil { scene.UpsertDevice(*device)
if mgr.devices == nil { mgr.devices = make(map[int]*models.Device) } mgr.devices[device.ID] = device } else { config.PublishChannel <- []models.Device{*device} }
if mgr.notifyCh != nil { close(mgr.notifyCh) mgr.notifyCh = nil }
return nil }
func (mgr *Manager) UpdateScene(data *models.Scene) { mgr.mu.Lock() for _, scene := range mgr.scenes { if scene.data.ID == data.ID { scene.UpdateScene(scene.data) } } mgr.mu.Unlock() }
func (mgr *Manager) FilterUnassigned(devices []models.Device) []models.Device { mgr.mu.Lock() defer mgr.mu.Unlock()
res := make([]models.Device, 0, len(devices)) for _, device := range devices { if mgr.allocations[device.ID] == nil { res = append(res, device) } }
return res }
func (mgr *Manager) Run(ctx context.Context) { devices, err := config.DeviceRepository().FetchByReference(ctx, models.RKAll, "") if err == nil { for _, device := range devices { _ = mgr.UpdateDevice(ctx, &device, nil) } }
ticker := time.NewTicker(time.Millisecond * 101) defer ticker.Stop()
mgr.mu.Lock() mgr.notifyCh = make(chan struct{}) mgr.mu.Unlock()
for { mgr.mu.Lock() if mgr.notifyCh == nil { mgr.notifyCh = make(chan struct{}) } notifyCh := mgr.notifyCh mgr.mu.Unlock()
select { case <-ticker.C: case <-notifyCh: case <-ctx.Done(): return }
timeout, cancel := context.WithTimeout(ctx, time.Millisecond*100)
var updates []models.Device mgr.mu.Lock() for _, device := range mgr.devices { if device == nil { continue }
if mgr.reScene(timeout, device) { updates = append(updates, *device) } }
for _, scene := range mgr.scenes { if scene.Due() { scene.Run() }
updates = append(updates, scene.Flush()...) } mgr.mu.Unlock()
cancel()
if len(updates) > 0 { log.Println("Updating", len(updates), "devices") config.PublishChannel <- updates } } }
func (mgr *Manager) reScene(ctx context.Context, device *models.Device) bool { scene, err := mgr.findScene(ctx, device.SceneAssignments, nil) if err != nil { return false }
if mgr.allocations[device.ID] != nil && mgr.allocations[device.ID] != scene { mgr.allocations[device.ID].RemoveDevice(*device) } if mgr.allocations[device.ID] != scene { if mgr.allocations == nil { mgr.allocations = make(map[int]*Scene, 8) }
mgr.allocations[device.ID] = scene
if scene == nil { return true }
mgr.allocations[device.ID].UpsertDevice(*device) }
return false }
func (mgr *Manager) findScene(ctx context.Context, assignments []models.DeviceSceneAssignment, data *models.Scene) (*Scene, error) { if len(assignments) == 0 { return nil, nil }
var selected *models.DeviceSceneAssignment for _, assignment := range assignments { pos := int64(time.Since(assignment.StartTime) / time.Millisecond) if assignment.DurationMS == 0 || (pos >= -1 && pos < assignment.DurationMS) { selected = &assignment } } if selected == nil { return nil, nil }
for _, scene := range mgr.scenes { if scene.group == selected.Group && scene.startTime.Equal(selected.StartTime) && selected.DurationMS == scene.duration.Milliseconds() { return scene, nil } }
if data == nil { for i := range mgr.scenes { if mgr.scenes[i].data.ID == selected.SceneID { data = &mgr.scenes[i].data break } } }
if data == nil { var err error data, err = config.SceneRepository().Find(ctx, selected.SceneID) if err != nil { return nil, err } }
scene := &Scene{ startTime: selected.StartTime, duration: time.Duration(selected.DurationMS) * time.Millisecond, lastRun: 0, due: true, group: selected.Group, data: *data, roleList: make([][]models.Device, len(data.Roles)), deviceMap: make(map[int]*models.Device, 8), roleMap: make(map[int]int, 8), changes: make(map[int]models.NewDeviceState, 8), }
mgr.scenes = append(mgr.scenes, scene)
return scene, nil }
var globalManager = &Manager{}
func GlobalManager() *Manager { return globalManager }
|