You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 

175 lines
3.5 KiB

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)
}