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.

107 lines
2.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. "sync"
  9. )
  10. func NewResolver() *Resolver {
  11. return &Resolver{
  12. pointers: make([]*device.Pointer, 0, 128),
  13. }
  14. }
  15. // Resolver implements both device.Resolver and lucifer3.Service. There should be only one of them as it keeps its
  16. // state from event-sourcery.
  17. type Resolver struct {
  18. mu sync.Mutex
  19. pointers []*device.Pointer
  20. }
  21. func (r *Resolver) ensure(id string) *device.Pointer {
  22. for _, ptr := range r.pointers {
  23. if ptr.ID == id {
  24. return ptr
  25. }
  26. }
  27. r.pointers = append(r.pointers, &device.Pointer{
  28. ID: id,
  29. })
  30. return r.pointers[len(r.pointers)-1]
  31. }
  32. func (r *Resolver) resolve(pattern string) []*device.Pointer {
  33. g, err := glob.Compile(pattern, ':')
  34. if err != nil {
  35. return []*device.Pointer{}
  36. }
  37. res := make([]*device.Pointer, 0, 32)
  38. for _, ptr := range r.pointers {
  39. if ptr.Matches(g) {
  40. res = append(res, ptr)
  41. }
  42. }
  43. return res
  44. }
  45. func (r *Resolver) Resolve(pattern string) []device.Pointer {
  46. g, err := glob.Compile(pattern, ':')
  47. if err != nil {
  48. return []device.Pointer{}
  49. }
  50. r.mu.Lock()
  51. defer r.mu.Unlock()
  52. res := make([]device.Pointer, 0, 32)
  53. for _, ptr := range r.pointers {
  54. if ptr.Matches(g) {
  55. res = append(res, *ptr)
  56. }
  57. }
  58. return res
  59. }
  60. func (r *Resolver) Active() bool {
  61. return true
  62. }
  63. func (r *Resolver) HandleEvent(_ *lucifer3.EventBus, event lucifer3.Event) {
  64. switch event := event.(type) {
  65. // On HardwareState, use the internal name if (and only if) it is not already named.
  66. case events.HardwareState:
  67. r.mu.Lock()
  68. ptr := r.ensure(event.ID)
  69. if event.InternalName != "" && ptr.Name() == "" {
  70. ptr.AddAlias("lucifer:name:" + event.InternalName)
  71. }
  72. r.mu.Unlock()
  73. }
  74. }
  75. func (r *Resolver) HandleCommand(_ *lucifer3.EventBus, command lucifer3.Command) {
  76. switch command := command.(type) {
  77. case commands.AddAlias:
  78. r.mu.Lock()
  79. for _, ptr := range r.resolve(command.Match) {
  80. ptr.AddAlias(command.Alias)
  81. }
  82. r.mu.Unlock()
  83. case commands.RemoveAlias:
  84. r.mu.Lock()
  85. for _, ptr := range r.resolve(command.Match) {
  86. ptr.AddAlias(command.Alias)
  87. }
  88. r.mu.Unlock()
  89. }
  90. }