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.

190 lines
3.7 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) Resolve(pattern string) []device.Pointer {
  39. r.mu.Lock()
  40. defer r.mu.Unlock()
  41. g := r.patternToGlob(pattern)
  42. if g == nil {
  43. return []device.Pointer{}
  44. }
  45. res := make([]device.Pointer, 0, 32)
  46. for _, ptr := range r.pointers {
  47. if ptr.Matches(g) {
  48. res = append(res, *ptr)
  49. }
  50. }
  51. return res
  52. }
  53. func (r *Resolver) Active() bool {
  54. return true
  55. }
  56. func (r *Resolver) HandleEvent(_ *lucifer3.EventBus, event lucifer3.Event) {
  57. switch event := event.(type) {
  58. // On HardwareState, use the internal name if (and only if) it is not already named.
  59. case events.HardwareState:
  60. r.mu.Lock()
  61. ptr := r.ensure(event.ID)
  62. if event.InternalName != "" && ptr.Name() == "" {
  63. ptr.AddAlias("lucifer:name:" + event.InternalName)
  64. r.sortPointers()
  65. }
  66. r.mu.Unlock()
  67. }
  68. }
  69. func (r *Resolver) HandleCommand(bus *lucifer3.EventBus, command lucifer3.Command) {
  70. var aliasEvents []lucifer3.Event
  71. switch command := command.(type) {
  72. case commands.AddAlias:
  73. r.mu.Lock()
  74. for _, ptr := range r.resolve(command.Match) {
  75. added, removed := ptr.AddAlias(command.Alias)
  76. if added != nil {
  77. aliasEvents = append(aliasEvents, events.AliasAdded{
  78. ID: ptr.ID,
  79. Alias: *added,
  80. })
  81. }
  82. if removed != nil {
  83. aliasEvents = append(aliasEvents, events.AliasRemoved{
  84. ID: ptr.ID,
  85. Alias: *removed,
  86. })
  87. }
  88. }
  89. if strings.HasPrefix(command.Alias, "lucifer:name:") {
  90. r.sortPointers()
  91. }
  92. r.mu.Unlock()
  93. case commands.RemoveAlias:
  94. r.mu.Lock()
  95. for _, ptr := range r.resolve(command.Match) {
  96. if ptr.RemoveAlias(command.Alias) {
  97. aliasEvents = append(aliasEvents, events.AliasRemoved{
  98. ID: ptr.ID,
  99. Alias: command.Alias,
  100. })
  101. }
  102. }
  103. if strings.HasPrefix(command.Alias, "lucifer:name:") {
  104. r.sortPointers()
  105. }
  106. r.mu.Unlock()
  107. }
  108. if len(aliasEvents) > 0 {
  109. go bus.RunEvents(aliasEvents)
  110. }
  111. }
  112. func (r *Resolver) ensure(id string) *device.Pointer {
  113. for _, ptr := range r.pointers {
  114. if ptr.ID == id {
  115. return ptr
  116. }
  117. }
  118. r.pointers = append(r.pointers, &device.Pointer{
  119. ID: id,
  120. })
  121. return r.pointers[len(r.pointers)-1]
  122. }
  123. func (r *Resolver) patternToGlob(pattern string) glob.Glob {
  124. if cached, ok := r.patternCache[pattern]; ok {
  125. return cached
  126. }
  127. g, err := glob.Compile(pattern, ':')
  128. if err != nil {
  129. return nil
  130. }
  131. // Keep this in bounds. Random cache deletions after 128 length.
  132. if len(r.patternCache) > 128 {
  133. i := 64
  134. for k := range r.patternCache {
  135. if i == 0 {
  136. break
  137. }
  138. delete(r.patternCache, k)
  139. i -= 1
  140. }
  141. }
  142. // Cache it
  143. r.patternCache[pattern] = g
  144. return g
  145. }
  146. func (r *Resolver) sortPointers() {
  147. sort.Slice(r.pointers, func(i, j int) bool {
  148. ni := r.pointers[i].Name()
  149. nj := r.pointers[j].Name()
  150. if ni == nj {
  151. return r.pointers[i].ID < r.pointers[j].ID
  152. }
  153. if ni == "" {
  154. return false
  155. }
  156. if nj == "" {
  157. return true
  158. }
  159. return ni < nj
  160. })
  161. }