|
|
@ -1,27 +1,84 @@ |
|
|
|
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 |
|
|
|
assignments map[int]*Scene |
|
|
|
allocations map[int]*Scene |
|
|
|
devices map[int]*models.Device |
|
|
|
scenes []*Scene |
|
|
|
|
|
|
|
notifyCh chan struct{} |
|
|
|
} |
|
|
|
|
|
|
|
func (mgr *Manager) UpdateDevice(device *models.Device) { |
|
|
|
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) GetUnassigned(devices []models.Device) []models.Device { |
|
|
|
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 _, ok := mgr.assignments[device.ID]; !ok { |
|
|
|
if mgr.allocations[device.ID] == nil { |
|
|
|
res = append(res, device) |
|
|
|
} |
|
|
|
} |
|
|
@ -29,8 +86,153 @@ func (mgr *Manager) GetUnassigned(devices []models.Device) []models.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 Get() *Manager { |
|
|
|
func GlobalManager() *Manager { |
|
|
|
return globalManager |
|
|
|
} |
xxxxxxxxxx