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.

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