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.

203 lines
3.9 KiB

2 years ago
2 years ago
2 years ago
2 years ago
  1. package services
  2. import (
  3. lucifer3 "git.aiterp.net/lucifer3/server"
  4. "git.aiterp.net/lucifer3/server/commands"
  5. "git.aiterp.net/lucifer3/server/device"
  6. "git.aiterp.net/lucifer3/server/events"
  7. "github.com/gobwas/glob"
  8. "sort"
  9. "strings"
  10. "sync"
  11. )
  12. func NewResolver() *Resolver {
  13. return &Resolver{
  14. pointers: make([]*device.Pointer, 0, 128),
  15. patternCache: make(map[string]glob.Glob),
  16. }
  17. }
  18. // Resolver implements both device.Resolver and lucifer3.Service. There should be only one of them as it keeps its
  19. // state from event-sourcery.
  20. type Resolver struct {
  21. mu sync.Mutex
  22. pointers []*device.Pointer
  23. patternCache map[string]glob.Glob
  24. }
  25. func (r *Resolver) resolve(pattern string) []*device.Pointer {
  26. g := r.patternToGlob(pattern)
  27. if g == nil {
  28. return []*device.Pointer{}
  29. }
  30. res := make([]*device.Pointer, 0, 32)
  31. for _, ptr := range r.pointers {
  32. if ptr.Matches(g) {
  33. res = append(res, ptr)
  34. }
  35. }
  36. return res
  37. }
  38. func (r *Resolver) GetByID(id string) *device.Pointer {
  39. r.mu.Lock()
  40. defer r.mu.Unlock()
  41. for _, ptr := range r.pointers {
  42. if ptr.ID == id {
  43. return ptr
  44. }
  45. }
  46. return nil
  47. }
  48. func (r *Resolver) Resolve(pattern string) []device.Pointer {
  49. r.mu.Lock()
  50. defer r.mu.Unlock()
  51. g := r.patternToGlob(pattern)
  52. if g == nil {
  53. return []device.Pointer{}
  54. }
  55. res := make([]device.Pointer, 0, 32)
  56. for _, ptr := range r.pointers {
  57. if ptr.Matches(g) {
  58. res = append(res, *ptr)
  59. }
  60. }
  61. return res
  62. }
  63. func (r *Resolver) Active() bool {
  64. return true
  65. }
  66. func (r *Resolver) HandleEvent(_ *lucifer3.EventBus, event lucifer3.Event) {
  67. switch event := event.(type) {
  68. // On HardwareState, use the internal name if (and only if) it is not already named.
  69. case events.HardwareState:
  70. r.mu.Lock()
  71. ptr := r.ensure(event.ID)
  72. if event.InternalName != "" && ptr.Name() == "" {
  73. ptr.AddAlias("lucifer:name:" + event.InternalName)
  74. r.sortPointers()
  75. }
  76. r.mu.Unlock()
  77. }
  78. }
  79. func (r *Resolver) HandleCommand(bus *lucifer3.EventBus, command lucifer3.Command) {
  80. var aliasEvents []lucifer3.Event
  81. switch command := command.(type) {
  82. case commands.AddAlias:
  83. r.mu.Lock()
  84. for _, ptr := range r.resolve(command.Match) {
  85. added, removed := ptr.AddAlias(command.Alias)
  86. if added != nil {
  87. aliasEvents = append(aliasEvents, events.AliasAdded{
  88. ID: ptr.ID,
  89. Alias: *added,
  90. })
  91. }
  92. if removed != nil {
  93. aliasEvents = append(aliasEvents, events.AliasRemoved{
  94. ID: ptr.ID,
  95. Alias: *removed,
  96. })
  97. }
  98. }
  99. if strings.HasPrefix(command.Alias, "lucifer:name:") {
  100. r.sortPointers()
  101. }
  102. r.mu.Unlock()
  103. case commands.RemoveAlias:
  104. r.mu.Lock()
  105. for _, ptr := range r.resolve(command.Match) {
  106. if ptr.RemoveAlias(command.Alias) {
  107. aliasEvents = append(aliasEvents, events.AliasRemoved{
  108. ID: ptr.ID,
  109. Alias: command.Alias,
  110. })
  111. }
  112. }
  113. if strings.HasPrefix(command.Alias, "lucifer:name:") {
  114. r.sortPointers()
  115. }
  116. r.mu.Unlock()
  117. }
  118. if len(aliasEvents) > 0 {
  119. go bus.RunEvents(aliasEvents)
  120. }
  121. }
  122. func (r *Resolver) ensure(id string) *device.Pointer {
  123. for _, ptr := range r.pointers {
  124. if ptr.ID == id {
  125. return ptr
  126. }
  127. }
  128. r.pointers = append(r.pointers, &device.Pointer{
  129. ID: id,
  130. })
  131. return r.pointers[len(r.pointers)-1]
  132. }
  133. func (r *Resolver) patternToGlob(pattern string) glob.Glob {
  134. if cached, ok := r.patternCache[pattern]; ok {
  135. return cached
  136. }
  137. g, err := glob.Compile(pattern, ':')
  138. if err != nil {
  139. return nil
  140. }
  141. // Keep this in bounds. Random cache deletions after 128 length.
  142. if len(r.patternCache) > 128 {
  143. i := 64
  144. for k := range r.patternCache {
  145. if i == 0 {
  146. break
  147. }
  148. delete(r.patternCache, k)
  149. i -= 1
  150. }
  151. }
  152. // Cache it
  153. r.patternCache[pattern] = g
  154. return g
  155. }
  156. func (r *Resolver) sortPointers() {
  157. sort.Slice(r.pointers, func(i, j int) bool {
  158. ni := r.pointers[i].Name()
  159. nj := r.pointers[j].Name()
  160. if ni == nj {
  161. return r.pointers[i].ID < r.pointers[j].ID
  162. }
  163. if ni == "" {
  164. return false
  165. }
  166. if nj == "" {
  167. return true
  168. }
  169. return ni < nj
  170. })
  171. }