|
|
package scene
import ( "context" "git.aiterp.net/lucifer/new-server/app/config" "git.aiterp.net/lucifer/new-server/models" "math" "time" )
type Scene struct { startTime time.Time duration time.Duration lastRun int64 due bool group string data models.Scene roleList [][]models.Device deviceMap map[int]*models.Device roleMap map[int]int changes map[int]models.NewDeviceState }
func (s *Scene) UpdateScene(scene models.Scene) { s.data = scene s.roleMap = make(map[int]int)
for _, device := range s.deviceMap { s.moveDevice(*device, false) }
for i, role := range s.data.Roles { role.ApplyOrder(s.roleList[i]) }
s.Run() }
func (s *Scene) UpsertDevice(device models.Device) { s.deviceMap[device.ID] = &device s.moveDevice(device, true) }
func (s *Scene) RemoveDevice(device models.Device) { roleIndex, hasRole := s.roleMap[device.ID]
delete(s.deviceMap, device.ID) delete(s.roleMap, device.ID)
if hasRole { s.runRole(roleIndex) } }
func (s *Scene) Due() bool { return s.due || s.intervalNumber() != s.lastRun }
func (s *Scene) Flush() []models.Device { res := make([]models.Device, 0, 8) for key, change := range s.changes { if s.deviceMap[key] != nil { device := *s.deviceMap[key] err := device.SetState(change) if err != nil { continue }
res = append(res, device) }
delete(s.changes, key) }
return res }
func (s *Scene) Run() { s.lastRun = s.intervalNumber() s.due = false
for index := range s.roleList { s.runRole(index) } }
func (s *Scene) runRole(index int) { role := s.data.Roles[index] devices := s.roleList[index]
intervalNumber := s.intervalNumber() intervalMax := s.intervalMax()
for i, device := range devices { newState, changed := role.ApplyEffect(&device, models.SceneRunContext{ Index: i, Length: len(devices), IntervalNumber: intervalNumber, IntervalMax: intervalMax, })
if !changed { go func() { _ = config.DeviceRepository().Save(context.Background(), &device, models.SMState) }() }
s.changes[device.ID] = newState } }
func (s *Scene) moveDevice(device models.Device, run bool) { oldRole, hasOldRole := s.roleMap[device.ID] newRole := s.data.RoleIndex(&device)
// If it doesn't move between roles, just update and reorder it.
if hasOldRole && oldRole == newRole { for i, device2 := range s.roleList[newRole] { if device2.ID == device.ID { s.roleList[newRole][i] = device } }
if run { s.data.Roles[newRole].ApplyOrder(s.roleList[newRole]) s.runRole(newRole) }
return }
// Take it out of the old role.
if hasOldRole { for i, device2 := range s.roleList[oldRole] { if device2.ID == device.ID { s.roleList[oldRole] = append(s.roleList[oldRole][:i], s.roleList[oldRole][i+1:]...) break } }
if run { s.runRole(oldRole) } }
if newRole != -1 { s.roleMap[device.ID] = newRole s.roleList[newRole] = append(s.roleList[newRole], device)
if run { s.data.Roles[newRole].ApplyOrder(s.roleList[newRole]) s.runRole(newRole) } }
s.due = true }
func (s *Scene) intervalNumber() int64 { intervalDuration := time.Duration(s.data.IntervalMS) * time.Millisecond if intervalDuration == 0 { return 0 }
return int64(math.Round(float64(time.Since(s.startTime)) / float64(intervalDuration))) }
func (s *Scene) intervalMax() int64 { intervalDuration := time.Duration(s.data.IntervalMS) * time.Millisecond if intervalDuration == 0 { return 1 }
return int64(s.duration / intervalDuration) }
|