refactor variable system to be the responsibility of effect enforcer.

This commit is contained in:
2023-01-15 22:56:37 +01:00
parent 4ec8918308
commit d2b2244e2f
11 changed files with 126 additions and 219 deletions
+85 -48
View File
@@ -7,9 +7,7 @@ import (
"git.aiterp.net/lucifer3/server/events"
"git.aiterp.net/lucifer3/server/internal/color"
"git.aiterp.net/lucifer3/server/internal/gentools"
"git.aiterp.net/lucifer3/server/services/variables"
"github.com/google/uuid"
"strings"
"sync"
"sync/atomic"
"time"
@@ -24,6 +22,8 @@ func NewService(resolver device.Resolver, sceneMap device.SceneMap) lucifer3.Act
supportFlags: make(map[string]device.SupportFlags),
colorFlags: make(map[string]device.ColorFlags),
temperatures: make(map[string]float64),
motions: make(map[string]float64),
}
return s
@@ -36,7 +36,8 @@ type effectEnforcer struct {
supportFlags map[string]device.SupportFlags
colorFlags map[string]device.ColorFlags
variables variables.Variables
temperatures map[string]float64
motions map[string]float64
started uint32
@@ -48,7 +49,7 @@ func (s *effectEnforcer) Active() bool {
return true
}
func (s *effectEnforcer) HandleEvent(_ *lucifer3.EventBus, event lucifer3.Event) {
func (s *effectEnforcer) HandleEvent(bus *lucifer3.EventBus, event lucifer3.Event) {
switch event := event.(type) {
case events.HardwareState:
s.mu.Lock()
@@ -75,19 +76,21 @@ func (s *effectEnforcer) HandleEvent(_ *lucifer3.EventBus, event lucifer3.Event)
}
s.mu.Unlock()
case variables.PropertyPatch:
case events.DeviceAssigned:
s.triggerVariableEffects(bus, event.DeviceID)
case events.MotionSensed:
s.mu.Lock()
s.variables = s.variables.WithPropertyPatch(event)
s.motions[event.ID] = event.SecondsSince
s.mu.Unlock()
case events.AliasAdded:
s.triggerVariableEffects()
case events.AliasRemoved:
s.triggerVariableEffects()
case events.MotionSensed:
s.triggerVariableEffects()
s.triggerVariableEffects(bus, event.ID)
case events.TemperatureChanged:
s.triggerVariableEffects()
s.mu.Lock()
s.temperatures[event.ID] = event.Temperature
s.mu.Unlock()
s.triggerVariableEffects(bus, event.ID)
}
}
@@ -159,11 +162,68 @@ func (s *effectEnforcer) HandleCommand(bus *lucifer3.EventBus, command lucifer3.
}
}
func (s *effectEnforcer) triggerVariableEffects() {
func (s *effectEnforcer) triggerVariableEffects(bus *lucifer3.EventBus, id string) {
now := time.Now()
s.mu.Lock()
for _, run := range s.list {
found := false
for _, id2 := range run.ids {
if id2 == id {
found = true
break
}
}
if !found {
continue
}
run.variables = map[string]float64{}
totalMotion := 0.0
motionSamples := 0
minMotion := 0.0
maxMotion := 0.0
totalTemperature := 0.0
temperatureSamples := 0
minTemperature := 0.0
maxTemperature := 0.0
for _, id := range run.ids {
if motion, ok := s.motions[id]; ok {
totalMotion += motion
motionSamples += 1
if motion < minMotion || motionSamples == 1 {
minMotion = motion
}
if motion > maxMotion {
maxMotion = motion
}
}
if temperature, ok := s.temperatures[id]; ok {
totalTemperature += temperature
temperatureSamples += 1
if temperature < minTemperature || temperatureSamples == 1 {
minTemperature = temperature
}
if temperature > maxTemperature {
maxTemperature = temperature
}
}
}
if temperatureSamples > 0 {
run.variables["temperature.avg"] = totalTemperature / float64(temperatureSamples)
run.variables["temperature.min"] = minTemperature
run.variables["temperature.max"] = maxTemperature
}
if motionSamples > 0 {
run.variables["motion.avg"] = totalMotion / float64(motionSamples)
run.variables["motion.min"] = minMotion
run.variables["motion.max"] = maxMotion
}
bus.RunEvent(events.AssignmentVariables{ID: run.id, Map: gentools.CopyMap(run.variables)})
if _, ok := run.effect.(lucifer3.VariableEffect); ok {
run.due = now
}
@@ -200,37 +260,13 @@ func (s *effectEnforcer) runLoop(bus *lucifer3.EventBus) {
state := run.effect.State(j, len(run.ids), run.round)
if vEff, ok := run.effect.(lucifer3.VariableEffect); ok {
variableName := strings.Split(vEff.VariableName(), ".")
if len(variableName) == 0 {
variableName = append(variableName, "avg")
}
var value *variables.AvgMinMax
switch variableName[0] {
case "temperature":
if value2, ok := s.variables.Temperature[run.match]; ok {
value = &value2
}
case "motion":
if value2, ok := s.variables.Motion[run.match]; ok {
value = &value2
}
}
if value != nil {
switch variableName[1] {
case "min":
state = vEff.VariableState(j, len(run.ids), value.Min)
case "max":
state = vEff.VariableState(j, len(run.ids), value.Max)
case "avg":
state = vEff.VariableState(j, len(run.ids), value.Avg)
}
variableName := vEff.VariableName()
if variable, ok := run.variables[variableName]; ok {
state = vEff.VariableState(j, len(run.ids), variable)
}
}
batch[id] = state
}
if freq := run.effect.Frequency(); freq > 0 {
@@ -312,13 +348,14 @@ func (s *effectEnforcer) runLoop(bus *lucifer3.EventBus) {
}
type effectEnforcerRun struct {
id uuid.UUID
match string
due time.Time
ids []string
effect lucifer3.Effect
dead bool
round int
id uuid.UUID
match string
due time.Time
ids []string
effect lucifer3.Effect
dead bool
round int
variables map[string]float64
}
// remove takes out an id from the effect, and returns whether the effect is empty.