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.

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