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

  1. package scene
  2. import (
  3. "context"
  4. "git.aiterp.net/lucifer/new-server/app/config"
  5. "git.aiterp.net/lucifer/new-server/models"
  6. "math"
  7. "time"
  8. )
  9. type Scene struct {
  10. startTime time.Time
  11. duration time.Duration
  12. lastRun int64
  13. due bool
  14. group string
  15. data models.Scene
  16. roleList [][]models.Device
  17. deviceMap map[int]*models.Device
  18. roleMap map[int]int
  19. changes map[int]models.NewDeviceState
  20. }
  21. func (s *Scene) UpdateScene(scene models.Scene) {
  22. s.data = scene
  23. s.roleMap = make(map[int]int)
  24. for _, device := range s.deviceMap {
  25. s.moveDevice(*device, false)
  26. }
  27. for i, role := range s.data.Roles {
  28. role.ApplyOrder(s.roleList[i])
  29. }
  30. s.Run()
  31. }
  32. func (s *Scene) UpsertDevice(device models.Device) {
  33. s.deviceMap[device.ID] = &device
  34. s.moveDevice(device, true)
  35. }
  36. func (s *Scene) RemoveDevice(device models.Device) {
  37. roleIndex, hasRole := s.roleMap[device.ID]
  38. delete(s.deviceMap, device.ID)
  39. delete(s.roleMap, device.ID)
  40. if hasRole {
  41. s.runRole(roleIndex)
  42. }
  43. }
  44. func (s *Scene) Due() bool {
  45. return s.due || s.intervalNumber() != s.lastRun
  46. }
  47. func (s *Scene) Flush() []models.Device {
  48. res := make([]models.Device, 0, 8)
  49. for key, change := range s.changes {
  50. if s.deviceMap[key] != nil {
  51. device := *s.deviceMap[key]
  52. err := device.SetState(change)
  53. if err != nil {
  54. continue
  55. }
  56. res = append(res, device)
  57. }
  58. delete(s.changes, key)
  59. }
  60. return res
  61. }
  62. func (s *Scene) Run() {
  63. s.lastRun = s.intervalNumber()
  64. s.due = false
  65. for index := range s.roleList {
  66. s.runRole(index)
  67. }
  68. }
  69. func (s *Scene) runRole(index int) {
  70. role := s.data.Roles[index]
  71. devices := s.roleList[index]
  72. intervalNumber := s.intervalNumber()
  73. intervalMax := s.intervalMax()
  74. for i, device := range devices {
  75. newState, changed := role.ApplyEffect(&device, models.SceneRunContext{
  76. Index: i,
  77. Length: len(devices),
  78. IntervalNumber: intervalNumber,
  79. IntervalMax: intervalMax,
  80. })
  81. if !changed {
  82. go func() {
  83. _ = config.DeviceRepository().Save(context.Background(), &device, models.SMState)
  84. }()
  85. }
  86. s.changes[device.ID] = newState
  87. }
  88. }
  89. func (s *Scene) moveDevice(device models.Device, run bool) {
  90. oldRole, hasOldRole := s.roleMap[device.ID]
  91. newRole := s.data.RoleIndex(&device)
  92. // If it doesn't move between roles, just update and reorder it.
  93. if hasOldRole && oldRole == newRole {
  94. for i, device2 := range s.roleList[newRole] {
  95. if device2.ID == device.ID {
  96. s.roleList[newRole][i] = device
  97. }
  98. }
  99. if run {
  100. s.data.Roles[newRole].ApplyOrder(s.roleList[newRole])
  101. s.runRole(newRole)
  102. }
  103. return
  104. }
  105. // Take it out of the old role.
  106. if hasOldRole {
  107. for i, device2 := range s.roleList[oldRole] {
  108. if device2.ID == device.ID {
  109. s.roleList[oldRole] = append(s.roleList[oldRole][:i], s.roleList[oldRole][i+1:]...)
  110. break
  111. }
  112. }
  113. if run {
  114. s.runRole(oldRole)
  115. }
  116. }
  117. if newRole != -1 {
  118. s.roleMap[device.ID] = newRole
  119. s.roleList[newRole] = append(s.roleList[newRole], device)
  120. if run {
  121. s.data.Roles[newRole].ApplyOrder(s.roleList[newRole])
  122. s.runRole(newRole)
  123. }
  124. }
  125. s.due = true
  126. }
  127. func (s *Scene) intervalNumber() int64 {
  128. intervalDuration := time.Duration(s.data.IntervalMS) * time.Millisecond
  129. if intervalDuration == 0 {
  130. return 0
  131. }
  132. return int64(math.Round(float64(time.Since(s.startTime)) / float64(intervalDuration)))
  133. }
  134. func (s *Scene) intervalMax() int64 {
  135. intervalDuration := time.Duration(s.data.IntervalMS) * time.Millisecond
  136. if intervalDuration == 0 {
  137. return 1
  138. }
  139. return int64(s.duration / intervalDuration)
  140. }