package publisher import ( "git.aiterp.net/lucifer/new-server/models" "time" ) type Scene struct { data *models.Scene group string startTime time.Time endTime time.Time roleMap map[int]int roleList map[int][]models.Device lastStates map[int]models.DeviceState due bool lastInterval int64 } // UpdateScene updates the scene data and re-seats all devices. func (s *Scene) UpdateScene(data models.Scene) { devices := make([]models.Device, 0, 16) // Collect all devices into the undefined role (-1) for _, list := range s.roleList { for _, device := range list { devices = append(devices, device) s.roleMap[device.ID] = -1 } } s.roleList = map[int][]models.Device{-1: devices} // Update data and reset devices. s.data = &data for _, device := range append(devices[:0:0], devices...) { s.UpsertDevice(device) } } // UpsertDevice moves the device if necessary and updates its state. func (s *Scene) UpsertDevice(device models.Device) { if s.data == nil { s.roleMap[device.ID] = -1 s.roleList[-1] = append(s.roleList[-1], device) return } oldIndex, hasOldIndex := s.roleMap[device.ID] newIndex := s.data.RoleIndex(&device) s.roleMap[device.ID] = newIndex if hasOldIndex { for i, device2 := range s.roleList[oldIndex] { if device2.ID == device.ID { s.roleList[oldIndex] = append(s.roleList[oldIndex][:i], s.roleList[oldIndex][i+1:]...) break } } } s.due = true s.roleList[newIndex] = append(s.roleList[newIndex], device) if newIndex != -1 { s.data.Roles[newIndex].ApplyOrder(s.roleList[newIndex]) } } // RemoveDevice finds and remove a device. It's a noop if the device does not exist in this scene. func (s *Scene) RemoveDevice(device models.Device) { roleIndex, hasRoleIndex := s.roleMap[device.ID] if !hasRoleIndex { return } for i, device2 := range s.roleList[roleIndex] { if device2.ID == device.ID { s.roleList[roleIndex] = append(s.roleList[roleIndex][:i], s.roleList[roleIndex][i+1:]...) break } } s.due = true delete(s.roleMap, device.ID) delete(s.lastStates, device.ID) } func (s *Scene) Empty() bool { for _, list := range s.roleList { if len(list) > 0 { return false } } return true } func (s *Scene) Due() bool { if s.due { return true } if s.data.IntervalMS > 0 { interval := time.Duration(s.data.IntervalMS) * time.Millisecond return int64(time.Since(s.startTime)/interval) != s.lastInterval } return false } func (s *Scene) UnaffectedDevices() []models.Device { return append(s.roleList[-1][:0:0], s.roleList[-1]...) } func (s *Scene) AllDevices() []models.Device { res := make([]models.Device, 0, 16) for _, list := range s.roleList { res = append(res, list...) } return res } // Run runs the scene func (s *Scene) Run() []models.Device { if s.data == nil { return []models.Device{} } intervalNumber := int64(0) intervalMax := int64(1) if s.data.IntervalMS > 0 { interval := time.Duration(s.data.IntervalMS) * time.Millisecond intervalNumber = int64(time.Since(s.startTime) / interval) if !s.endTime.IsZero() { intervalMax = int64(s.endTime.Sub(s.startTime) / interval) } else { intervalMax = intervalNumber + 1 } } updatedDevices := make([]models.Device, 0, 16) for i, list := range s.roleList { if i == -1 { continue } role := s.data.Roles[i] for j, device := range list { newState := role.ApplyEffect(&device, models.SceneRunContext{ Index: j, Length: len(list), IntervalNumber: intervalNumber, IntervalMax: intervalMax, }) err := device.SetState(newState) if err != nil { continue } s.lastStates[device.ID] = device.State updatedDevices = append(updatedDevices, device) } } s.due = false s.lastInterval = intervalNumber return updatedDevices } func (s *Scene) LastState(id int) *models.DeviceState { lastState, ok := s.lastStates[id] if !ok { return nil } return &lastState }