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.

118 lines
2.9 KiB

  1. package script
  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/internal/formattools"
  7. "git.aiterp.net/lucifer3/server/internal/gentools"
  8. "sort"
  9. "sync"
  10. )
  11. type service struct {
  12. mu sync.Mutex
  13. resolver device.Resolver
  14. variables *Variables
  15. scripts map[string]*Script
  16. }
  17. func (s *service) Active() bool {
  18. return true
  19. }
  20. func (s *service) HandleEvent(_ *lucifer3.EventBus, _ lucifer3.Event) {}
  21. func (s *service) HandleCommand(bus *lucifer3.EventBus, command lucifer3.Command) {
  22. switch command := command.(type) {
  23. case UpdateScript:
  24. s.mu.Lock()
  25. s.scripts[command.Name] = &Script{
  26. Name: command.Name,
  27. Lines: command.Lines,
  28. }
  29. s.mu.Unlock()
  30. case ExecuteScript:
  31. s.mu.Lock()
  32. script := s.scripts[command.Name]
  33. s.mu.Unlock()
  34. if script == nil {
  35. break
  36. }
  37. variables := s.variables.Get()
  38. devices := s.resolver.Resolve(command.Match)
  39. sort.Slice(devices, func(i, j int) bool {
  40. return devices[i].ID < devices[j].ID
  41. })
  42. s.runScript(bus, script.Lines, command.Match, map[string]bool{}, devices, variables)
  43. }
  44. }
  45. func NewService(resolver device.Resolver, variables *Variables) lucifer3.Service {
  46. return &service{
  47. resolver: resolver,
  48. variables: variables,
  49. scripts: map[string]*Script{},
  50. }
  51. }
  52. func (s *service) runScript(bus *lucifer3.EventBus, lines []Line, match string, matchedDevices map[string]bool, devices []device.Pointer, variables VariableSet) {
  53. for _, line := range lines {
  54. if line.If != nil {
  55. devicesIDs := gentools.Map(devices, func(d device.Pointer) string { return d.ID })
  56. if line.If.Condition.Check(variables, match, devicesIDs) {
  57. s.runScript(bus, line.If.Then, match, matchedDevices, devices, variables)
  58. } else {
  59. s.runScript(bus, line.If.Else, match, matchedDevices, devices, variables)
  60. }
  61. } else if line.Assign != nil {
  62. matcher := s.resolver.CompileMatcher(line.Assign.Match)
  63. matched := make([]string, 0)
  64. for _, dev := range devices {
  65. if matchedDevices[dev.ID] {
  66. continue
  67. }
  68. if dev.Matches(matcher) {
  69. matchedDevices[dev.ID] = true
  70. matched = append(matched, dev.ID)
  71. }
  72. }
  73. if len(matched) > 0 {
  74. bus.RunCommand(commands.Assign{
  75. Match: formattools.CompactMatch(matched),
  76. Effect: line.Assign.Effect.Effect,
  77. })
  78. }
  79. } else {
  80. if line.Set != nil {
  81. switch line.Set.Scope {
  82. case "devices":
  83. bus.RunCommand(SetVariable{
  84. Devices: gentools.Map(devices, func(d device.Pointer) string {
  85. return d.ID
  86. }),
  87. Key: line.Set.Key,
  88. Value: line.Set.Value,
  89. })
  90. case "match":
  91. bus.RunCommand(SetVariable{
  92. Match: gentools.Ptr(match),
  93. Key: line.Set.Key,
  94. Value: line.Set.Value,
  95. })
  96. case "global":
  97. bus.RunCommand(SetVariable{
  98. Key: line.Set.Key,
  99. Value: line.Set.Value,
  100. })
  101. }
  102. }
  103. }
  104. }
  105. }