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.

238 lines
4.9 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. "log"
  7. "sync"
  8. "time"
  9. )
  10. type Manager struct {
  11. mu sync.Mutex
  12. allocations map[int]*Scene
  13. devices map[int]*models.Device
  14. scenes []*Scene
  15. notifyCh chan struct{}
  16. }
  17. func (mgr *Manager) UpdateDevice(ctx context.Context, device *models.Device, data *models.Scene) error {
  18. mgr.mu.Lock()
  19. defer mgr.mu.Unlock()
  20. scene, err := mgr.findScene(ctx, device.SceneAssignments, data)
  21. if err != nil {
  22. return err
  23. }
  24. if mgr.allocations[device.ID] != nil && mgr.allocations[device.ID] != scene {
  25. mgr.allocations[device.ID].RemoveDevice(*device)
  26. }
  27. if mgr.allocations[device.ID] != scene {
  28. if mgr.allocations == nil {
  29. mgr.allocations = make(map[int]*Scene, 8)
  30. }
  31. mgr.allocations[device.ID] = scene
  32. if scene != nil {
  33. scene.due = true
  34. }
  35. }
  36. if scene != nil {
  37. scene.UpsertDevice(*device)
  38. if mgr.devices == nil {
  39. mgr.devices = make(map[int]*models.Device)
  40. }
  41. mgr.devices[device.ID] = device
  42. } else {
  43. config.PublishChannel <- []models.Device{*device}
  44. }
  45. if mgr.notifyCh != nil {
  46. close(mgr.notifyCh)
  47. mgr.notifyCh = nil
  48. }
  49. return nil
  50. }
  51. func (mgr *Manager) UpdateScene(data *models.Scene) {
  52. mgr.mu.Lock()
  53. for _, scene := range mgr.scenes {
  54. if scene.data.ID == data.ID {
  55. scene.UpdateScene(scene.data)
  56. }
  57. }
  58. mgr.mu.Unlock()
  59. }
  60. func (mgr *Manager) FilterUnassigned(devices []models.Device) []models.Device {
  61. mgr.mu.Lock()
  62. defer mgr.mu.Unlock()
  63. res := make([]models.Device, 0, len(devices))
  64. for _, device := range devices {
  65. if mgr.allocations[device.ID] == nil {
  66. res = append(res, device)
  67. }
  68. }
  69. return res
  70. }
  71. func (mgr *Manager) Run(ctx context.Context) {
  72. devices, err := config.DeviceRepository().FetchByReference(ctx, models.RKAll, "")
  73. if err == nil {
  74. for _, device := range devices {
  75. _ = mgr.UpdateDevice(ctx, &device, nil)
  76. }
  77. }
  78. ticker := time.NewTicker(time.Millisecond * 101)
  79. defer ticker.Stop()
  80. mgr.mu.Lock()
  81. mgr.notifyCh = make(chan struct{})
  82. mgr.mu.Unlock()
  83. for {
  84. mgr.mu.Lock()
  85. if mgr.notifyCh == nil {
  86. mgr.notifyCh = make(chan struct{})
  87. }
  88. notifyCh := mgr.notifyCh
  89. mgr.mu.Unlock()
  90. select {
  91. case <-ticker.C:
  92. case <-notifyCh:
  93. case <-ctx.Done():
  94. return
  95. }
  96. timeout, cancel := context.WithTimeout(ctx, time.Millisecond*100)
  97. var updates []models.Device
  98. mgr.mu.Lock()
  99. for _, device := range mgr.devices {
  100. if device == nil {
  101. continue
  102. }
  103. if mgr.reScene(timeout, device) {
  104. updates = append(updates, *device)
  105. }
  106. }
  107. for _, scene := range mgr.scenes {
  108. if scene.Due() {
  109. scene.Run()
  110. }
  111. updates = append(updates, scene.Flush()...)
  112. }
  113. mgr.mu.Unlock()
  114. cancel()
  115. if len(updates) > 0 {
  116. log.Println("Updating", len(updates), "devices")
  117. config.PublishChannel <- updates
  118. }
  119. }
  120. }
  121. func (mgr *Manager) reScene(ctx context.Context, device *models.Device) bool {
  122. scene, err := mgr.findScene(ctx, device.SceneAssignments, nil)
  123. if err != nil {
  124. return false
  125. }
  126. if mgr.allocations[device.ID] != nil && mgr.allocations[device.ID] != scene {
  127. mgr.allocations[device.ID].RemoveDevice(*device)
  128. }
  129. if mgr.allocations[device.ID] != scene {
  130. if mgr.allocations == nil {
  131. mgr.allocations = make(map[int]*Scene, 8)
  132. }
  133. mgr.allocations[device.ID] = scene
  134. if scene == nil {
  135. return true
  136. }
  137. mgr.allocations[device.ID].UpsertDevice(*device)
  138. }
  139. return false
  140. }
  141. func (mgr *Manager) findScene(ctx context.Context, assignments []models.DeviceSceneAssignment, data *models.Scene) (*Scene, error) {
  142. if len(assignments) == 0 {
  143. return nil, nil
  144. }
  145. var selected *models.DeviceSceneAssignment
  146. for _, assignment := range assignments {
  147. pos := int64(time.Since(assignment.StartTime) / time.Millisecond)
  148. if assignment.DurationMS == 0 || (pos >= -1 && pos < assignment.DurationMS) {
  149. selected = &assignment
  150. }
  151. }
  152. if selected == nil {
  153. return nil, nil
  154. }
  155. for _, scene := range mgr.scenes {
  156. if scene.group == selected.Group && scene.startTime.Equal(selected.StartTime) && selected.DurationMS == scene.duration.Milliseconds() {
  157. return scene, nil
  158. }
  159. }
  160. if data == nil {
  161. for i := range mgr.scenes {
  162. if mgr.scenes[i].data.ID == selected.SceneID {
  163. data = &mgr.scenes[i].data
  164. break
  165. }
  166. }
  167. }
  168. if data == nil {
  169. var err error
  170. data, err = config.SceneRepository().Find(ctx, selected.SceneID)
  171. if err != nil {
  172. return nil, err
  173. }
  174. }
  175. scene := &Scene{
  176. startTime: selected.StartTime,
  177. duration: time.Duration(selected.DurationMS) * time.Millisecond,
  178. lastRun: 0,
  179. due: true,
  180. group: selected.Group,
  181. data: *data,
  182. roleList: make([][]models.Device, len(data.Roles)),
  183. deviceMap: make(map[int]*models.Device, 8),
  184. roleMap: make(map[int]int, 8),
  185. changes: make(map[int]models.NewDeviceState, 8),
  186. }
  187. mgr.scenes = append(mgr.scenes, scene)
  188. return scene, nil
  189. }
  190. var globalManager = &Manager{}
  191. func GlobalManager() *Manager {
  192. return globalManager
  193. }