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.

161 lines
4.2 KiB

1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
  1. package script
  2. import (
  3. "fmt"
  4. lucifer3 "git.aiterp.net/lucifer3/server"
  5. "git.aiterp.net/lucifer3/server/commands"
  6. "git.aiterp.net/lucifer3/server/device"
  7. "git.aiterp.net/lucifer3/server/internal/formattools"
  8. "git.aiterp.net/lucifer3/server/internal/gentools"
  9. "github.com/google/uuid"
  10. "sort"
  11. )
  12. type service struct {
  13. resolver device.Resolver
  14. variables *Variables
  15. scripts map[string]*Script
  16. triggers map[uuid.UUID]*Trigger
  17. }
  18. func (s *service) Active() bool {
  19. return true
  20. }
  21. func (s *service) HandleEvent(bus *lucifer3.EventBus, event lucifer3.Event) {
  22. for _, trig := range s.triggers {
  23. if trig.Check(event, s.resolver) {
  24. lines := append(make([]Line, 0, 64), trig.ScriptPre...)
  25. script := s.scripts[trig.ScriptName]
  26. if script != nil {
  27. lines = append(lines, script.Lines...)
  28. }
  29. lines = append(lines, trig.ScriptPost...)
  30. variables := s.variables.Get()
  31. devices := s.resolver.Resolve(trig.ScriptTarget)
  32. sort.Slice(devices, func(i, j int) bool {
  33. return devices[i].ID < devices[j].ID
  34. })
  35. fmt.Println("[TRIGGER] Running", trig.ID, "for", len(devices), "devices:", event.EventDescription())
  36. s.runScript(bus, lines, trig.ScriptTarget, map[string]bool{}, devices, variables)
  37. }
  38. }
  39. }
  40. func (s *service) HandleCommand(bus *lucifer3.EventBus, command lucifer3.Command) {
  41. switch command := command.(type) {
  42. case Update:
  43. s.scripts[command.Name] = &Script{
  44. Name: command.Name,
  45. Lines: command.Lines,
  46. }
  47. case Execute:
  48. script := s.scripts[command.Name]
  49. if script == nil {
  50. break
  51. }
  52. variables := s.variables.Get()
  53. devices := s.resolver.Resolve(command.Match)
  54. sort.Slice(devices, func(i, j int) bool {
  55. return devices[i].ID < devices[j].ID
  56. })
  57. s.runScript(bus, script.Lines, command.Match, map[string]bool{}, devices, variables)
  58. case UpdateTrigger:
  59. s.triggers[command.ID] = gentools.Ptr(Trigger(command))
  60. case DeleteTrigger:
  61. delete(s.triggers, command.ID)
  62. }
  63. }
  64. func NewService(resolver device.Resolver, variables *Variables) lucifer3.Service {
  65. return &service{
  66. resolver: resolver,
  67. variables: variables,
  68. triggers: make(map[uuid.UUID]*Trigger, 8),
  69. scripts: make(map[string]*Script, 8),
  70. }
  71. }
  72. func (s *service) runScript(bus *lucifer3.EventBus, lines []Line, match string, matchedDevices map[string]bool, devices []device.Pointer, variables VariableSet) {
  73. for _, line := range lines {
  74. if line.If != nil {
  75. devicesIDs := gentools.Map(devices, func(d device.Pointer) string { return d.ID })
  76. if line.If.Condition.Check(variables, match, devicesIDs) {
  77. s.runScript(bus, line.If.Then, match, matchedDevices, devices, variables)
  78. } else {
  79. s.runScript(bus, line.If.Else, match, matchedDevices, devices, variables)
  80. }
  81. } else if line.Assign != nil {
  82. matcher := s.resolver.CompileMatcher(line.Assign.Match)
  83. matched := make([]string, 0)
  84. for _, dev := range devices {
  85. if matchedDevices[dev.ID] {
  86. continue
  87. }
  88. if line.Assign.Match == "*" || dev.Matches(matcher) {
  89. matchedDevices[dev.ID] = true
  90. matched = append(matched, dev.ID)
  91. }
  92. }
  93. if len(matched) > 0 {
  94. bus.RunCommand(commands.Assign{
  95. Match: formattools.CompactMatch(matched),
  96. Effect: line.Assign.Effect.Effect,
  97. })
  98. }
  99. } else if line.Set != nil {
  100. var sv *SetVariable
  101. switch line.Set.Scope {
  102. case "devices":
  103. sv = &SetVariable{
  104. Devices: gentools.Map(devices, func(d device.Pointer) string {
  105. return d.ID
  106. }),
  107. Key: line.Set.Key,
  108. Value: line.Set.Value,
  109. }
  110. bus.RunCommand(sv)
  111. case "match":
  112. sv = &SetVariable{
  113. Match: gentools.Ptr(match),
  114. Key: line.Set.Key,
  115. Value: line.Set.Value,
  116. }
  117. case "global":
  118. sv = &SetVariable{
  119. Match: gentools.Ptr(match),
  120. Key: line.Set.Key,
  121. Value: line.Set.Value,
  122. }
  123. }
  124. if sv != nil {
  125. variables = variables.With(*sv)
  126. bus.RunCommand(*sv)
  127. }
  128. } else if line.Select != nil {
  129. matcher := s.resolver.CompileMatcher(line.Select.Match)
  130. matched := make([]device.Pointer, 0)
  131. for _, dev := range devices {
  132. if dev.Matches(matcher) {
  133. matched = append(matched, dev)
  134. }
  135. }
  136. if len(matched) > 0 {
  137. s.runScript(bus, line.Select.Then, match, matchedDevices, matched, variables)
  138. }
  139. }
  140. }
  141. }