diff --git a/core/keys/site.go b/core/keys/site.go index 63988eca64..4ffbb14714 100644 --- a/core/keys/site.go +++ b/core/keys/site.go @@ -4,7 +4,6 @@ const ( Aux = "aux" AuxPower = "auxPower" Currency = "currency" - Ext = "ext" GreenShareHome = "greenShareHome" GreenShareLoadpoints = "greenShareLoadpoints" GridConfigured = "gridConfigured" @@ -30,6 +29,7 @@ const ( TariffPriceLoadpoints = "tariffPriceLoadpoints" Vehicles = "vehicles" Circuits = "circuits" + Ext = "ext" // meters GridMeter = "gridMeter" diff --git a/core/site.go b/core/site.go index 7534f74ae6..1e1b2df0d2 100644 --- a/core/site.go +++ b/core/site.go @@ -434,17 +434,25 @@ func (site *Site) publishDelta(key string, val interface{}) { site.publish(key, val) } -func (site *Site) collectMeters(key string, meters []api.Meter) []meterMeasurement { +// updatePvMeters updates pv meters. All measurements are optional. +func (site *Site) updatePvMeters() { + if len(site.pvMeters) == 0 { + return + } + var wg sync.WaitGroup - mm := make([]meterMeasurement, len(meters)) + + mm := make([]meterMeasurement, len(site.pvMeters)) fun := func(i int, meter api.Meter) { // power power, err := backoff.RetryWithData(meter.CurrentPower, bo()) if err == nil { - site.log.DEBUG.Printf("%s %d power: %.0fW", key, i+1, power) + if power < -500 { + site.log.WARN.Printf("pv %d power: %.0fW is negative - check configuration if sign is correct", i+1, power) + } } else { - site.log.ERROR.Printf("%s %d power: %v", key, i+1, err) + site.log.ERROR.Printf("pv %d power: %v", i+1, err) } // energy (production) @@ -452,96 +460,171 @@ func (site *Site) collectMeters(key string, meters []api.Meter) []meterMeasureme if m, ok := meter.(api.MeterEnergy); err == nil && ok { energy, err = m.TotalEnergy() if err != nil { - site.log.ERROR.Printf("%s %d energy: %v", key, i+1, err) + site.log.ERROR.Printf("pv %d energy: %v", i+1, err) } } + var excessDC float64 + var excessStr string + if m, ok := meter.(api.MaxACPower); ok { + if dc := m.MaxACPower() - power; dc < 0 && power > 0 { + excessDC = -dc + excessStr = fmt.Sprintf(" (includes %.0fW excess DC)", -dc) + } + } + + if len(site.pvMeters) > 1 { + site.log.DEBUG.Printf("pv %d power: %.0fW"+excessStr, i+1, power) + } + mm[i] = meterMeasurement{ - Power: power, - Energy: energy, + Power: power, + Energy: energy, + ExcessDCPower: excessDC, } wg.Done() } - wg.Add(len(meters)) - for i, meter := range meters { + wg.Add(len(site.pvMeters)) + for i, meter := range site.pvMeters { go fun(i, meter) } wg.Wait() - return mm + site.pvPower = lo.Reduce(mm, func(acc float64, m meterMeasurement, _ int) float64 { + return acc + max(0, m.Power) + }, 0) + site.excessDCPower = lo.Reduce(mm, func(acc float64, m meterMeasurement, _ int) float64 { + return acc - math.Abs(m.ExcessDCPower) + }, 0) + totalEnergy := lo.Reduce(mm, func(acc float64, m meterMeasurement, _ int) float64 { + return acc + m.Energy + }, 0) + + var excessStr string + if site.excessDCPower < 0 { + excessStr = fmt.Sprintf(" (includes %.0fW excess DC)", -site.excessDCPower) + } + + site.log.DEBUG.Printf("pv power: %.0fW"+excessStr, site.pvPower) + site.publish(keys.PvPower, site.pvPower) + site.publish(keys.PvEnergy, totalEnergy) + site.publish(keys.Pv, mm) } -// updatePvMeters updates pv meters. All measurements are optional. -func (site *Site) updatePvMeters() { - if len(site.pvMeters) == 0 { +// updateAuxMeters updates aux meters +func (site *Site) updateAuxMeters() { + if len(site.auxMeters) == 0 { return } - mm := site.collectMeters("pv", site.pvMeters) + var wg sync.WaitGroup - for i, meter := range site.pvMeters { - power := mm[i].Power + mm := make([]meterMeasurement, len(site.auxMeters)) - if power < -500 { - site.log.WARN.Printf("pv %d power: %.0fW is negative - check configuration if sign is correct", i+1, power) + fun := func(i int, meter api.Meter) { + if power, err := meter.CurrentPower(); err == nil { + mm[i].Power = power + site.log.DEBUG.Printf("aux power %d: %.0fW", i+1, power) + } else { + site.log.ERROR.Printf("aux meter %d: %v", i+1, err) } - if m, ok := meter.(api.MaxACPower); ok { - if dc := m.MaxACPower() - power; dc < 0 && power > 0 { - mm[i].ExcessDCPower = -dc - site.log.DEBUG.Printf("pv %d excess DC: %.0fW", i+1, -dc) - } - } + wg.Done() } - site.pvPower = lo.Reduce(mm, func(acc float64, m meterMeasurement, _ int) float64 { - return acc + max(0, m.Power) - }, 0) - site.excessDCPower = lo.Reduce(mm, func(acc float64, m meterMeasurement, _ int) float64 { - return acc - math.Abs(m.ExcessDCPower) - }, 0) - totalEnergy := lo.Reduce(mm, func(acc float64, m meterMeasurement, _ int) float64 { - return acc + m.Energy + wg.Add(len(site.auxMeters)) + for i, meter := range site.auxMeters { + go fun(i, meter) + } + wg.Wait() + + site.auxPower = lo.Reduce(mm, func(acc float64, m meterMeasurement, _ int) float64 { + return acc + m.Power }, 0) - if len(site.pvMeters) > 1 { - var excessStr string - if site.excessDCPower < 0 { - excessStr = fmt.Sprintf(" (includes %.0fW excess DC)", -site.excessDCPower) + site.log.DEBUG.Printf("aux power: %.0fW", site.auxPower) + site.publish(keys.AuxPower, site.auxPower) + site.publish(keys.Aux, mm) +} + +// updateExtMeters updates ext meters +func (site *Site) updateExtMeters() { + if len(site.extMeters) == 0 { + return + } + + mm := make([]meterMeasurement, len(site.extMeters)) + + for i, meter := range site.extMeters { + // ext power + power, err := backoff.RetryWithData(meter.CurrentPower, bo()) + if err != nil { + site.log.ERROR.Printf("ext meter %d power: %v", i+1, err) } - site.log.DEBUG.Printf("pv power: %.0fW"+excessStr, site.pvPower) + // ext energy + var energy float64 + if m, ok := meter.(api.MeterEnergy); err == nil && ok { + energy, err = m.TotalEnergy() + if err != nil { + site.log.ERROR.Printf("ext meter %d energy: %v", i+1, err) + } + } + + mm[i] = meterMeasurement{ + Power: power, + Energy: energy, + } } - site.publish(keys.PvPower, site.pvPower) - site.publish(keys.PvEnergy, totalEnergy) - site.publish(keys.Pv, mm) + // Publishing will be done in separate PR } // updateBatteryMeters updates battery meters -func (site *Site) updateBatteryMeters() { +func (site *Site) updateBatteryMeters() error { if len(site.batteryMeters) == 0 { - return + return nil } - mm := site.collectMeters("battery", site.batteryMeters) - bm := make([]batteryMeasurement, len(site.batteryMeters)) + var eg errgroup.Group + + mm := make([]batteryMeasurement, len(site.batteryMeters)) + + fun := func(i int, meter api.Meter) error { + power, err := backoff.RetryWithData(meter.CurrentPower, bo()) + if err != nil { + // power is required- return on error + return fmt.Errorf("battery %d power: %v", i+1, err) + } + + if len(site.batteryMeters) > 1 { + site.log.DEBUG.Printf("battery %d power: %.0fW", i+1, power) + } + + // battery energy (discharge) + var energy float64 + if m, ok := meter.(api.MeterEnergy); ok { + energy, err = m.TotalEnergy() + if err != nil { + site.log.ERROR.Printf("battery %d energy: %v", i+1, err) + } + } - for i, meter := range site.batteryMeters { // battery soc and capacity var batSoc, capacity float64 - var err error + if meter, ok := meter.(api.Battery); ok { + batSoc, err = soc.Guard(meter.Soc()) - if m, ok := meter.(api.Battery); ok { - batSoc, err = soc.Guard(m.Soc()) if err == nil { - if m, ok := m.(api.BatteryCapacity); ok { + if m, ok := meter.(api.BatteryCapacity); ok { capacity = m.Capacity() } - site.log.DEBUG.Printf("battery %d soc: %.0f%%", i+1, batSoc) + if len(site.batteryMeters) > 1 { + site.log.DEBUG.Printf("battery %d soc: %.0f%%", i+1, batSoc) + } } else { site.log.ERROR.Printf("battery %d soc: %v", i+1, err) } @@ -549,16 +632,26 @@ func (site *Site) updateBatteryMeters() { _, controllable := meter.(api.BatteryController) - bm[i] = batteryMeasurement{ - Power: mm[i].Power, - Energy: mm[i].Energy, + mm[i] = batteryMeasurement{ + Power: power, + Energy: energy, Soc: batSoc, Capacity: capacity, Controllable: controllable, } + + return nil + } + + for i, meter := range site.batteryMeters { + eg.Go(func() error { return fun(i, meter) }) + } + + if err := eg.Wait(); err != nil { + return err } - site.batterySoc = lo.Reduce(bm, func(acc float64, m batteryMeasurement, _ int) float64 { + site.batterySoc = lo.Reduce(mm, func(acc float64, m batteryMeasurement, _ int) float64 { // weigh soc by capacity weighedSoc := m.Soc if m.Capacity > 0 { @@ -566,7 +659,7 @@ func (site *Site) updateBatteryMeters() { } return acc + weighedSoc }, 0) - totalCapacity := lo.Reduce(bm, func(acc float64, m batteryMeasurement, _ int) float64 { + totalCapacity := lo.Reduce(mm, func(acc float64, m batteryMeasurement, _ int) float64 { return acc + m.Capacity }, 0) @@ -576,50 +669,23 @@ func (site *Site) updateBatteryMeters() { } site.batterySoc /= totalCapacity - site.batteryPower = lo.Reduce(bm, func(acc float64, m batteryMeasurement, _ int) float64 { + site.log.DEBUG.Printf("battery soc: %.0f%%", math.Round(site.batterySoc)) + site.publish(keys.BatteryCapacity, totalCapacity) + site.publish(keys.BatterySoc, site.batterySoc) + + site.batteryPower = lo.Reduce(mm, func(acc float64, m batteryMeasurement, _ int) float64 { return acc + m.Power }, 0) - totalEnergy := lo.Reduce(bm, func(acc float64, m batteryMeasurement, _ int) float64 { + totalEnergy := lo.Reduce(mm, func(acc float64, m batteryMeasurement, _ int) float64 { return acc + m.Energy }, 0) - if len(site.batteryMeters) > 1 { - site.log.DEBUG.Printf("battery power: %.0fW", site.batteryPower) - site.log.DEBUG.Printf("battery soc: %.0f%%", math.Round(site.batterySoc)) - } - - site.publish(keys.BatteryCapacity, totalCapacity) - site.publish(keys.BatterySoc, site.batterySoc) - + site.log.DEBUG.Printf("battery power: %.0fW", site.batteryPower) site.publish(keys.BatteryPower, site.batteryPower) site.publish(keys.BatteryEnergy, totalEnergy) site.publish(keys.Battery, mm) -} -// updateAuxMeters updates aux meters -func (site *Site) updateAuxMeters() { - if len(site.auxMeters) == 0 { - return - } - - mm := site.collectMeters("aux", site.auxMeters) - site.auxPower = lo.Reduce(mm, func(acc float64, m meterMeasurement, _ int) float64 { - return acc + m.Power - }, 0) - - site.log.DEBUG.Printf("aux power: %.0fW", site.auxPower) - site.publish(keys.AuxPower, site.auxPower) - site.publish(keys.Aux, mm) -} - -// updateExtMeters updates ext meters -func (site *Site) updateExtMeters() { - if len(site.extMeters) == 0 { - return - } - - mm := site.collectMeters("ext", site.extMeters) - site.publish(keys.Ext, mm) + return nil } // updateGridMeter updates grid meter @@ -676,10 +742,10 @@ func (site *Site) updateMeters() error { var eg errgroup.Group eg.Go(func() error { site.updatePvMeters(); return nil }) - eg.Go(func() error { site.updateBatteryMeters(); return nil }) eg.Go(func() error { site.updateAuxMeters(); return nil }) eg.Go(func() error { site.updateExtMeters(); return nil }) + eg.Go(site.updateBatteryMeters) eg.Go(site.updateGridMeter) return eg.Wait()