wip
This commit is contained in:
parent
5093eaa8b4
commit
843392fb7e
2 changed files with 123 additions and 189 deletions
|
|
@ -4,6 +4,7 @@ const (
|
|||
Aux = "aux"
|
||||
AuxPower = "auxPower"
|
||||
Currency = "currency"
|
||||
Ext = "ext"
|
||||
GreenShareHome = "greenShareHome"
|
||||
GreenShareLoadpoints = "greenShareLoadpoints"
|
||||
GridConfigured = "gridConfigured"
|
||||
|
|
@ -29,7 +30,6 @@ const (
|
|||
TariffPriceLoadpoints = "tariffPriceLoadpoints"
|
||||
Vehicles = "vehicles"
|
||||
Circuits = "circuits"
|
||||
Ext = "ext"
|
||||
|
||||
// meters
|
||||
GridMeter = "gridMeter"
|
||||
|
|
|
|||
310
core/site.go
310
core/site.go
|
|
@ -434,25 +434,17 @@ func (site *Site) publishDelta(key string, val interface{}) {
|
|||
site.publish(key, val)
|
||||
}
|
||||
|
||||
// updatePvMeters updates pv meters. All measurements are optional.
|
||||
func (site *Site) updatePvMeters() {
|
||||
if len(site.pvMeters) == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
func (site *Site) collectMeters(key string, meters []api.Meter) []meterMeasurement {
|
||||
var wg sync.WaitGroup
|
||||
|
||||
mm := make([]meterMeasurement, len(site.pvMeters))
|
||||
mm := make([]meterMeasurement, len(meters))
|
||||
|
||||
fun := func(i int, meter api.Meter) {
|
||||
// power
|
||||
power, err := backoff.RetryWithData(meter.CurrentPower, bo())
|
||||
if err == nil {
|
||||
if power < -500 {
|
||||
site.log.WARN.Printf("pv %d power: %.0fW is negative - check configuration if sign is correct", i+1, power)
|
||||
}
|
||||
site.log.DEBUG.Printf("%s %d power: %.0fW", key, i+1, power)
|
||||
} else {
|
||||
site.log.ERROR.Printf("pv %d power: %v", i+1, err)
|
||||
site.log.ERROR.Printf("%s %d power: %v", key, i+1, err)
|
||||
}
|
||||
|
||||
// energy (production)
|
||||
|
|
@ -460,38 +452,50 @@ func (site *Site) updatePvMeters() {
|
|||
if m, ok := meter.(api.MeterEnergy); err == nil && ok {
|
||||
energy, err = m.TotalEnergy()
|
||||
if err != nil {
|
||||
site.log.ERROR.Printf("pv %d energy: %v", i+1, err)
|
||||
site.log.ERROR.Printf("%s %d energy: %v", key, 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,
|
||||
ExcessDCPower: excessDC,
|
||||
Power: power,
|
||||
Energy: energy,
|
||||
}
|
||||
|
||||
wg.Done()
|
||||
}
|
||||
|
||||
wg.Add(len(site.pvMeters))
|
||||
for i, meter := range site.pvMeters {
|
||||
wg.Add(len(meters))
|
||||
for i, meter := range meters {
|
||||
go fun(i, meter)
|
||||
}
|
||||
wg.Wait()
|
||||
|
||||
return mm
|
||||
}
|
||||
|
||||
// updatePvMeters updates pv meters. All measurements are optional.
|
||||
func (site *Site) updatePvMeters() {
|
||||
if len(site.pvMeters) == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
mm := site.collectMeters("pv", site.pvMeters)
|
||||
|
||||
for i, meter := range site.pvMeters {
|
||||
power := mm[i].Power
|
||||
|
||||
if power < -500 {
|
||||
site.log.WARN.Printf("pv %d power: %.0fW is negative - check configuration if sign is correct", i+1, power)
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
site.pvPower = lo.Reduce(mm, func(acc float64, m meterMeasurement, _ int) float64 {
|
||||
return acc + max(0, m.Power)
|
||||
}, 0)
|
||||
|
|
@ -502,44 +506,103 @@ func (site *Site) updatePvMeters() {
|
|||
return acc + m.Energy
|
||||
}, 0)
|
||||
|
||||
var excessStr string
|
||||
if site.excessDCPower < 0 {
|
||||
excessStr = fmt.Sprintf(" (includes %.0fW excess DC)", -site.excessDCPower)
|
||||
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("pv power: %.0fW"+excessStr, site.pvPower)
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
// updateBatteryMeters updates battery meters
|
||||
func (site *Site) updateBatteryMeters() {
|
||||
if len(site.batteryMeters) == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
mm := site.collectMeters("battery", site.batteryMeters)
|
||||
bm := make([]batteryMeasurement, len(site.batteryMeters))
|
||||
|
||||
for i, meter := range site.batteryMeters {
|
||||
// battery soc and capacity
|
||||
var batSoc, capacity float64
|
||||
var err error
|
||||
|
||||
if m, ok := meter.(api.Battery); ok {
|
||||
batSoc, err = soc.Guard(m.Soc())
|
||||
if err == nil {
|
||||
if m, ok := m.(api.BatteryCapacity); ok {
|
||||
capacity = m.Capacity()
|
||||
}
|
||||
|
||||
site.log.DEBUG.Printf("battery %d soc: %.0f%%", i+1, batSoc)
|
||||
} else {
|
||||
site.log.ERROR.Printf("battery %d soc: %v", i+1, err)
|
||||
}
|
||||
}
|
||||
|
||||
_, controllable := meter.(api.BatteryController)
|
||||
|
||||
bm[i] = batteryMeasurement{
|
||||
Power: mm[i].Power,
|
||||
Energy: mm[i].Energy,
|
||||
Soc: batSoc,
|
||||
Capacity: capacity,
|
||||
Controllable: controllable,
|
||||
}
|
||||
}
|
||||
|
||||
site.batterySoc = lo.Reduce(bm, func(acc float64, m batteryMeasurement, _ int) float64 {
|
||||
// weigh soc by capacity
|
||||
weighedSoc := m.Soc
|
||||
if m.Capacity > 0 {
|
||||
weighedSoc *= m.Capacity
|
||||
}
|
||||
return acc + weighedSoc
|
||||
}, 0)
|
||||
totalCapacity := lo.Reduce(bm, func(acc float64, m batteryMeasurement, _ int) float64 {
|
||||
return acc + m.Capacity
|
||||
}, 0)
|
||||
|
||||
// convert weighed socs to total soc
|
||||
if totalCapacity == 0 {
|
||||
totalCapacity = float64(len(site.batteryMeters))
|
||||
}
|
||||
site.batterySoc /= totalCapacity
|
||||
|
||||
site.batteryPower = lo.Reduce(bm, func(acc float64, m batteryMeasurement, _ int) float64 {
|
||||
return acc + m.Power
|
||||
}, 0)
|
||||
totalEnergy := lo.Reduce(bm, 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.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
|
||||
}
|
||||
|
||||
var wg sync.WaitGroup
|
||||
|
||||
mm := make([]meterMeasurement, len(site.auxMeters))
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
wg.Done()
|
||||
}
|
||||
|
||||
wg.Add(len(site.auxMeters))
|
||||
for i, meter := range site.auxMeters {
|
||||
go fun(i, meter)
|
||||
}
|
||||
wg.Wait()
|
||||
|
||||
mm := site.collectMeters("aux", site.auxMeters)
|
||||
site.auxPower = lo.Reduce(mm, func(acc float64, m meterMeasurement, _ int) float64 {
|
||||
return acc + m.Power
|
||||
}, 0)
|
||||
|
|
@ -555,137 +618,8 @@ func (site *Site) updateExtMeters() {
|
|||
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)
|
||||
}
|
||||
|
||||
// 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,
|
||||
}
|
||||
}
|
||||
|
||||
// Publishing will be done in separate PR
|
||||
}
|
||||
|
||||
// updateBatteryMeters updates battery meters
|
||||
func (site *Site) updateBatteryMeters() error {
|
||||
if len(site.batteryMeters) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
// battery soc and capacity
|
||||
var batSoc, capacity float64
|
||||
if meter, ok := meter.(api.Battery); ok {
|
||||
batSoc, err = soc.Guard(meter.Soc())
|
||||
|
||||
if err == nil {
|
||||
if m, ok := meter.(api.BatteryCapacity); ok {
|
||||
capacity = m.Capacity()
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
_, controllable := meter.(api.BatteryController)
|
||||
|
||||
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(mm, func(acc float64, m batteryMeasurement, _ int) float64 {
|
||||
// weigh soc by capacity
|
||||
weighedSoc := m.Soc
|
||||
if m.Capacity > 0 {
|
||||
weighedSoc *= m.Capacity
|
||||
}
|
||||
return acc + weighedSoc
|
||||
}, 0)
|
||||
totalCapacity := lo.Reduce(mm, func(acc float64, m batteryMeasurement, _ int) float64 {
|
||||
return acc + m.Capacity
|
||||
}, 0)
|
||||
|
||||
// convert weighed socs to total soc
|
||||
if totalCapacity == 0 {
|
||||
totalCapacity = float64(len(site.batteryMeters))
|
||||
}
|
||||
site.batterySoc /= totalCapacity
|
||||
|
||||
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(mm, func(acc float64, m batteryMeasurement, _ int) float64 {
|
||||
return acc + m.Energy
|
||||
}, 0)
|
||||
|
||||
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)
|
||||
|
||||
return nil
|
||||
mm := site.collectMeters("ext", site.extMeters)
|
||||
site.publish(keys.Ext, mm)
|
||||
}
|
||||
|
||||
// updateGridMeter updates grid meter
|
||||
|
|
@ -742,10 +676,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()
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue