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.

186 lines
3.9 KiB

  1. package publisher
  2. import (
  3. "git.aiterp.net/lucifer/new-server/models"
  4. "time"
  5. )
  6. type Scene struct {
  7. data *models.Scene
  8. group string
  9. startTime time.Time
  10. endTime time.Time
  11. roleMap map[int]int
  12. roleList map[int][]models.Device
  13. lastStates map[int]models.DeviceState
  14. due bool
  15. lastInterval int64
  16. }
  17. // UpdateScene updates the scene data and re-seats all devices.
  18. func (s *Scene) UpdateScene(data models.Scene) {
  19. devices := make([]models.Device, 0, 16)
  20. // Collect all devices into the undefined role (-1)
  21. for _, list := range s.roleList {
  22. for _, device := range list {
  23. devices = append(devices, device)
  24. s.roleMap[device.ID] = -1
  25. }
  26. }
  27. s.roleList = map[int][]models.Device{-1: devices}
  28. // Update data and reset devices.
  29. s.data = &data
  30. for _, device := range append(devices[:0:0], devices...) {
  31. s.UpsertDevice(device)
  32. }
  33. }
  34. // UpsertDevice moves the device if necessary and updates its state.
  35. func (s *Scene) UpsertDevice(device models.Device) {
  36. if s.data == nil {
  37. s.roleMap[device.ID] = -1
  38. s.roleList[-1] = append(s.roleList[-1], device)
  39. return
  40. }
  41. oldIndex, hasOldIndex := s.roleMap[device.ID]
  42. newIndex := s.data.RoleIndex(&device)
  43. s.roleMap[device.ID] = newIndex
  44. if hasOldIndex {
  45. for i, device2 := range s.roleList[oldIndex] {
  46. if device2.ID == device.ID {
  47. s.roleList[oldIndex] = append(s.roleList[oldIndex][:i], s.roleList[oldIndex][i+1:]...)
  48. break
  49. }
  50. }
  51. }
  52. s.due = true
  53. s.roleList[newIndex] = append(s.roleList[newIndex], device)
  54. if newIndex != -1 {
  55. s.data.Roles[newIndex].ApplyOrder(s.roleList[newIndex])
  56. }
  57. }
  58. // RemoveDevice finds and remove a device. It's a noop if the device does not exist in this scene.
  59. func (s *Scene) RemoveDevice(device models.Device) {
  60. roleIndex, hasRoleIndex := s.roleMap[device.ID]
  61. if !hasRoleIndex {
  62. return
  63. }
  64. for i, device2 := range s.roleList[roleIndex] {
  65. if device2.ID == device.ID {
  66. s.roleList[roleIndex] = append(s.roleList[roleIndex][:i], s.roleList[roleIndex][i+1:]...)
  67. break
  68. }
  69. }
  70. s.due = true
  71. delete(s.roleMap, device.ID)
  72. delete(s.lastStates, device.ID)
  73. }
  74. func (s *Scene) Empty() bool {
  75. for _, list := range s.roleList {
  76. if len(list) > 0 {
  77. return false
  78. }
  79. }
  80. return true
  81. }
  82. func (s *Scene) Due() bool {
  83. if s.due {
  84. return true
  85. }
  86. if s.data.IntervalMS > 0 {
  87. interval := time.Duration(s.data.IntervalMS) * time.Millisecond
  88. return int64(time.Since(s.startTime)/interval) != s.lastInterval
  89. }
  90. return false
  91. }
  92. func (s *Scene) UnaffectedDevices() []models.Device {
  93. return append(s.roleList[-1][:0:0], s.roleList[-1]...)
  94. }
  95. func (s *Scene) AllDevices() []models.Device {
  96. res := make([]models.Device, 0, 16)
  97. for _, list := range s.roleList {
  98. res = append(res, list...)
  99. }
  100. return res
  101. }
  102. // Run runs the scene
  103. func (s *Scene) Run() []models.Device {
  104. if s.data == nil {
  105. return []models.Device{}
  106. }
  107. intervalNumber := int64(0)
  108. intervalMax := int64(1)
  109. if s.data.IntervalMS > 0 {
  110. interval := time.Duration(s.data.IntervalMS) * time.Millisecond
  111. intervalNumber = int64(time.Since(s.startTime) / interval)
  112. if !s.endTime.IsZero() {
  113. intervalMax = int64(s.endTime.Sub(s.startTime) / interval)
  114. } else {
  115. intervalMax = intervalNumber + 1
  116. }
  117. }
  118. updatedDevices := make([]models.Device, 0, 16)
  119. for i, list := range s.roleList {
  120. if i == -1 {
  121. continue
  122. }
  123. role := s.data.Roles[i]
  124. for j, device := range list {
  125. newState := role.ApplyEffect(&device, models.SceneRunContext{
  126. Index: j,
  127. Length: len(list),
  128. IntervalNumber: intervalNumber,
  129. IntervalMax: intervalMax,
  130. })
  131. err := device.SetState(newState)
  132. if err != nil {
  133. continue
  134. }
  135. s.lastStates[device.ID] = device.State
  136. updatedDevices = append(updatedDevices, device)
  137. }
  138. }
  139. s.due = false
  140. s.lastInterval = intervalNumber
  141. return updatedDevices
  142. }
  143. func (s *Scene) LastState(id int) *models.DeviceState {
  144. lastState, ok := s.lastStates[id]
  145. if !ok {
  146. return nil
  147. }
  148. return &lastState
  149. }