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.

166 lines
3.2 KiB

  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(_ *lucifer3.EventBus, command lucifer3.Command) {
  70. switch command := command.(type) {
  71. case commands.AddAlias:
  72. r.mu.Lock()
  73. for _, ptr := range r.resolve(command.Match) {
  74. ptr.AddAlias(command.Alias)
  75. }
  76. if strings.HasPrefix(command.Alias, "lucifer:name:") {
  77. r.sortPointers()
  78. }
  79. r.mu.Unlock()
  80. case commands.RemoveAlias:
  81. r.mu.Lock()
  82. for _, ptr := range r.resolve(command.Match) {
  83. ptr.RemoveAlias(command.Alias)
  84. }
  85. if strings.HasPrefix(command.Alias, "lucifer:name:") {
  86. r.sortPointers()
  87. }
  88. r.mu.Unlock()
  89. }
  90. }
  91. func (r *Resolver) ensure(id string) *device.Pointer {
  92. for _, ptr := range r.pointers {
  93. if ptr.ID == id {
  94. return ptr
  95. }
  96. }
  97. r.pointers = append(r.pointers, &device.Pointer{
  98. ID: id,
  99. })
  100. return r.pointers[len(r.pointers)-1]
  101. }
  102. func (r *Resolver) patternToGlob(pattern string) glob.Glob {
  103. if cached, ok := r.patternCache[pattern]; ok {
  104. return cached
  105. }
  106. g, err := glob.Compile(pattern, ':')
  107. if err != nil {
  108. return nil
  109. }
  110. // Keep this in bounds. Random cache deletions after 128 length.
  111. if len(r.patternCache) > 128 {
  112. i := 64
  113. for k := range r.patternCache {
  114. if i == 0 {
  115. break
  116. }
  117. delete(r.patternCache, k)
  118. i -= 1
  119. }
  120. }
  121. // Cache it
  122. r.patternCache[pattern] = g
  123. return g
  124. }
  125. func (r *Resolver) sortPointers() {
  126. sort.Slice(r.pointers, func(i, j int) bool {
  127. ni := r.pointers[i].Name()
  128. nj := r.pointers[j].Name()
  129. if ni == nj {
  130. return r.pointers[i].ID < r.pointers[j].ID
  131. }
  132. if ni == "" {
  133. return false
  134. }
  135. if nj == "" {
  136. return true
  137. }
  138. return ni < nj
  139. })
  140. }