chore: rename metrics import/export to energy/returnEnergy throughout collector and accumulator (#30303)
This commit is contained in:
parent
8106dc8b76
commit
03d4565c0b
7 changed files with 96 additions and 118 deletions
|
|
@ -9,12 +9,12 @@ import (
|
|||
)
|
||||
|
||||
type Accumulator struct {
|
||||
clock clock.Clock
|
||||
updated time.Time
|
||||
importMeter *float64 // kWh
|
||||
exportMeter *float64 // kWh
|
||||
Energy float64 `json:"energy"` // kWh
|
||||
ReturnEnergy float64 `json:"returnEnergy"` // kWh
|
||||
clock clock.Clock
|
||||
updated time.Time
|
||||
energyMeter *float64 // kWh
|
||||
returnEnergyMeter *float64 // kWh
|
||||
Energy float64 `json:"energy"` // kWh
|
||||
ReturnEnergy float64 `json:"returnEnergy"` // kWh
|
||||
}
|
||||
|
||||
func WithClock(clock clock.Clock) func(*Accumulator) {
|
||||
|
|
@ -37,63 +37,53 @@ func (m *Accumulator) Updated() time.Time {
|
|||
|
||||
func (m *Accumulator) String() string {
|
||||
b := new(bytes.Buffer)
|
||||
fmt.Fprintf(b, "Accumulated: %.3fkWh pos, %.3fkWh neg, updated: %v", m.Energy, m.ReturnEnergy, m.updated.Truncate(time.Second))
|
||||
if m.importMeter != nil || m.exportMeter != nil {
|
||||
fmt.Fprintf(b, " meter: ")
|
||||
if m.importMeter != nil {
|
||||
fmt.Fprintf(b, " %.3fkWh pos", *m.importMeter)
|
||||
fmt.Fprintf(b, "Accumulated: %.3fkWh energy, %.3fkWh return energy, updated: %v", m.Energy, m.ReturnEnergy, m.updated.Truncate(time.Second))
|
||||
if m.energyMeter != nil || m.returnEnergyMeter != nil {
|
||||
fmt.Fprintf(b, " energy total:")
|
||||
if m.energyMeter != nil {
|
||||
fmt.Fprintf(b, " %.3fkWh", *m.energyMeter)
|
||||
}
|
||||
if m.exportMeter != nil {
|
||||
fmt.Fprintf(b, " %.3fkWh pos", *m.exportMeter)
|
||||
if m.returnEnergyMeter != nil {
|
||||
fmt.Fprintf(b, " %.3fkWh return energy", *m.returnEnergyMeter)
|
||||
}
|
||||
}
|
||||
return b.String()
|
||||
}
|
||||
|
||||
// Imported returns the accumulated import energy in kWh
|
||||
func (m *Accumulator) Imported() float64 {
|
||||
return m.Energy
|
||||
}
|
||||
|
||||
// Exported returns the accumulated export energy in kWh
|
||||
func (m *Accumulator) Exported() float64 {
|
||||
return m.ReturnEnergy
|
||||
}
|
||||
|
||||
// SetImportMeterTotal adds the difference to the last total meter value in kWh
|
||||
func (m *Accumulator) SetImportMeterTotal(v float64) {
|
||||
// SetEnergyMeterTotal adds the difference to the last total meter value in kWh
|
||||
func (m *Accumulator) SetEnergyMeterTotal(v float64) {
|
||||
defer func() {
|
||||
m.updated = m.clock.Now()
|
||||
m.importMeter = new(v)
|
||||
m.energyMeter = new(v)
|
||||
}()
|
||||
|
||||
if m.importMeter == nil {
|
||||
if m.energyMeter == nil {
|
||||
return
|
||||
}
|
||||
|
||||
if v >= *m.importMeter {
|
||||
m.Energy += v - *m.importMeter
|
||||
if v >= *m.energyMeter {
|
||||
m.Energy += v - *m.energyMeter
|
||||
}
|
||||
}
|
||||
|
||||
// SetExportMeterTotal adds the difference to the last total meter value in kWh
|
||||
func (m *Accumulator) SetExportMeterTotal(v float64) {
|
||||
// SetReturnEnergyMeterTotal adds the difference to the last total meter value in kWh
|
||||
func (m *Accumulator) SetReturnEnergyMeterTotal(v float64) {
|
||||
defer func() {
|
||||
m.updated = m.clock.Now()
|
||||
m.exportMeter = new(v)
|
||||
m.returnEnergyMeter = new(v)
|
||||
}()
|
||||
|
||||
if m.exportMeter == nil {
|
||||
if m.returnEnergyMeter == nil {
|
||||
return
|
||||
}
|
||||
|
||||
if v >= *m.exportMeter {
|
||||
m.ReturnEnergy += v - *m.exportMeter
|
||||
if v >= *m.returnEnergyMeter {
|
||||
m.ReturnEnergy += v - *m.returnEnergyMeter
|
||||
}
|
||||
}
|
||||
|
||||
// AddImportEnergy adds the given energy in kWh to the positive meter
|
||||
func (m *Accumulator) AddImportEnergy(v float64) {
|
||||
// AddEnergy adds the given energy in kWh to the energy total
|
||||
func (m *Accumulator) AddEnergy(v float64) {
|
||||
defer func() { m.updated = m.clock.Now() }()
|
||||
|
||||
if m.updated.IsZero() {
|
||||
|
|
@ -103,8 +93,8 @@ func (m *Accumulator) AddImportEnergy(v float64) {
|
|||
m.Energy += v
|
||||
}
|
||||
|
||||
// AddExportEnergy adds the given energy in kWh to the negative meter
|
||||
func (m *Accumulator) AddExportEnergy(v float64) {
|
||||
// AddReturnEnergy adds the given energy in kWh to the return energy total
|
||||
func (m *Accumulator) AddReturnEnergy(v float64) {
|
||||
defer func() { m.updated = m.clock.Now() }()
|
||||
|
||||
if m.updated.IsZero() {
|
||||
|
|
@ -118,8 +108,8 @@ func (m *Accumulator) AddExportEnergy(v float64) {
|
|||
func (m *Accumulator) AddPower(v float64) {
|
||||
since := v * m.clock.Since(m.updated).Hours() / 1e3
|
||||
if v >= 0 {
|
||||
m.AddImportEnergy(since)
|
||||
m.AddEnergy(since)
|
||||
} else {
|
||||
m.AddExportEnergy(-since)
|
||||
m.AddReturnEnergy(-since)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -15,12 +15,12 @@ func TestMeterEnergyMeterTotal(t *testing.T) {
|
|||
|
||||
me := &Accumulator{clock: clock}
|
||||
|
||||
me.SetImportMeterTotal(10)
|
||||
assert.Equal(t, 0.0, me.Imported())
|
||||
me.SetImportMeterTotal(11)
|
||||
assert.Equal(t, 1.0, me.Imported())
|
||||
me.SetImportMeterTotal(11)
|
||||
assert.Equal(t, 1.0, me.Imported())
|
||||
me.SetEnergyMeterTotal(10)
|
||||
assert.Equal(t, 0.0, me.Energy)
|
||||
me.SetEnergyMeterTotal(11)
|
||||
assert.Equal(t, 1.0, me.Energy)
|
||||
me.SetEnergyMeterTotal(11)
|
||||
assert.Equal(t, 1.0, me.Energy)
|
||||
}
|
||||
|
||||
func TestMeterEnergyAddPower(t *testing.T) {
|
||||
|
|
@ -31,13 +31,13 @@ func TestMeterEnergyAddPower(t *testing.T) {
|
|||
|
||||
clock.Add(60 * time.Minute)
|
||||
me.AddPower(1e3)
|
||||
assert.Equal(t, 0.0, me.Imported())
|
||||
assert.Equal(t, 0.0, me.Energy)
|
||||
|
||||
clock.Add(60 * time.Minute)
|
||||
me.AddPower(1e3)
|
||||
assert.Equal(t, 1.0, me.Imported())
|
||||
assert.Equal(t, 1.0, me.Energy)
|
||||
|
||||
clock.Add(30 * time.Minute)
|
||||
me.AddPower(1e3)
|
||||
assert.Equal(t, 1.5, me.Imported())
|
||||
assert.Equal(t, 1.5, me.Energy)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -99,56 +99,44 @@ func (c *Collector) process(fun func()) error {
|
|||
}
|
||||
|
||||
func (c *Collector) persist() error {
|
||||
return persist(c.entity, c.started, c.accu.Imported(), c.accu.Exported())
|
||||
return persist(c.entity, c.started, c.accu.Energy, c.accu.ReturnEnergy)
|
||||
}
|
||||
|
||||
func (c *Collector) ImportProfile(from time.Time) (*[96]float64, error) {
|
||||
return importProfile(c.entity, from)
|
||||
func (c *Collector) EnergyProfile(from time.Time) (*[96]float64, error) {
|
||||
return energyProfile(c.entity, from)
|
||||
}
|
||||
|
||||
func (c *Collector) AddImportEnergy(v float64) error {
|
||||
func (c *Collector) SetEnergyMeterTotal(v float64) error {
|
||||
return c.process(func() {
|
||||
c.accu.AddImportEnergy(v)
|
||||
c.accu.SetEnergyMeterTotal(v)
|
||||
})
|
||||
}
|
||||
|
||||
func (c *Collector) AddExportEnergy(v float64) error {
|
||||
func (c *Collector) SetReturnEnergyMeterTotal(v float64) error {
|
||||
return c.process(func() {
|
||||
c.accu.AddExportEnergy(v)
|
||||
})
|
||||
}
|
||||
|
||||
func (c *Collector) SetImportMeterTotal(v float64) error {
|
||||
return c.process(func() {
|
||||
c.accu.SetImportMeterTotal(v)
|
||||
})
|
||||
}
|
||||
|
||||
func (c *Collector) SetExportMeterTotal(v float64) error {
|
||||
return c.process(func() {
|
||||
c.accu.SetExportMeterTotal(v)
|
||||
c.accu.SetReturnEnergyMeterTotal(v)
|
||||
})
|
||||
}
|
||||
|
||||
// AddEnergy adds energy using meter totals if available, falling back to power integration.
|
||||
func (c *Collector) AddEnergy(importTotal, exportTotal *float64, power float64) error {
|
||||
func (c *Collector) AddEnergy(energyTotal, returnEnergyTotal *float64, power float64) error {
|
||||
return c.process(func() {
|
||||
switch {
|
||||
case importTotal != nil && exportTotal != nil:
|
||||
c.accu.SetImportMeterTotal(*importTotal)
|
||||
c.accu.SetExportMeterTotal(*exportTotal)
|
||||
case importTotal != nil:
|
||||
// export via power integration (before meter updates clock)
|
||||
case energyTotal != nil && returnEnergyTotal != nil:
|
||||
c.accu.SetEnergyMeterTotal(*energyTotal)
|
||||
c.accu.SetReturnEnergyMeterTotal(*returnEnergyTotal)
|
||||
case energyTotal != nil:
|
||||
// return energy via power integration (before meter updates clock)
|
||||
if power < 0 {
|
||||
c.accu.AddPower(power)
|
||||
}
|
||||
c.accu.SetImportMeterTotal(*importTotal)
|
||||
case exportTotal != nil:
|
||||
// import via power integration (before meter updates clock)
|
||||
c.accu.SetEnergyMeterTotal(*energyTotal)
|
||||
case returnEnergyTotal != nil:
|
||||
// energy via power integration (before meter updates clock)
|
||||
if power >= 0 {
|
||||
c.accu.AddPower(power)
|
||||
}
|
||||
c.accu.SetExportMeterTotal(*exportTotal)
|
||||
c.accu.SetReturnEnergyMeterTotal(*returnEnergyTotal)
|
||||
default:
|
||||
c.accu.AddPower(power)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -25,18 +25,18 @@ func TestCollectorAddEnergy(t *testing.T) {
|
|||
|
||||
clock.Add(5 * time.Minute)
|
||||
require.NoError(t, col.AddEnergy(nil, nil, 1e3))
|
||||
require.Equal(t, 1e3*5/60/1e3, col.accu.Imported()) // kWh
|
||||
require.Equal(t, 1e3*5/60/1e3, col.accu.Energy) // kWh
|
||||
|
||||
clock.Add(5 * time.Minute)
|
||||
require.NoError(t, col.AddEnergy(nil, nil, 1e3))
|
||||
require.Equal(t, 0.0, col.accu.Imported()) // accumulator reset after 15 minutes
|
||||
require.Equal(t, 0.0, col.accu.Energy) // accumulator reset after 15 minutes
|
||||
|
||||
clock.Add(15 * time.Minute)
|
||||
require.NoError(t, col.AddEnergy(nil, nil, 1e3))
|
||||
require.Equal(t, 0.0, col.accu.Imported()) // accumulator reset after 15 minutes
|
||||
require.Equal(t, 0.0, col.accu.Energy) // accumulator reset after 15 minutes
|
||||
}
|
||||
|
||||
func TestCollectorAddEnergyWithImportMeter(t *testing.T) {
|
||||
func TestCollectorAddEnergyWithEnergyMeter(t *testing.T) {
|
||||
clock := clock.NewMock()
|
||||
|
||||
require.NoError(t, db.NewInstance("sqlite", ":memory:"))
|
||||
|
|
@ -48,20 +48,20 @@ func TestCollectorAddEnergyWithImportMeter(t *testing.T) {
|
|||
// first call: seeds meter, no delta yet
|
||||
clock.Add(5 * time.Minute)
|
||||
require.NoError(t, col.AddEnergy(new(50000.0), nil, 1e3))
|
||||
require.Equal(t, 0.0, col.accu.Imported())
|
||||
require.Equal(t, 0.0, col.accu.Energy)
|
||||
|
||||
// second call: meter delta of 0.5 kWh, power ignored for import
|
||||
// second call: meter delta of 0.5 kWh, power ignored for energy
|
||||
clock.Add(5 * time.Minute)
|
||||
require.NoError(t, col.AddEnergy(new(50000.5), nil, 1e3))
|
||||
require.Equal(t, 0.5, col.accu.Imported())
|
||||
require.Equal(t, 0.5, col.accu.Energy)
|
||||
|
||||
// implausible reading (decreased): ignored by guard
|
||||
clock.Add(5 * time.Minute)
|
||||
require.NoError(t, col.AddEnergy(new(49000.0), nil, 1e3))
|
||||
require.Equal(t, 0.0, col.accu.Imported()) // reset at slot boundary
|
||||
require.Equal(t, 0.0, col.accu.Energy) // reset at slot boundary
|
||||
}
|
||||
|
||||
func TestCollectorAddEnergyWithImportMeterAndExport(t *testing.T) {
|
||||
func TestCollectorAddEnergyWithEnergyMeterAndReturn(t *testing.T) {
|
||||
clock := clock.NewMock()
|
||||
|
||||
require.NoError(t, db.NewInstance("sqlite", ":memory:"))
|
||||
|
|
@ -70,24 +70,24 @@ func TestCollectorAddEnergyWithImportMeterAndExport(t *testing.T) {
|
|||
col, err := NewCollector("baz", "baz", "", WithClock(clock))
|
||||
require.NoError(t, err)
|
||||
|
||||
// seed import meter
|
||||
// seed energy meter
|
||||
clock.Add(3 * time.Minute)
|
||||
require.NoError(t, col.AddEnergy(new(1000.0), nil, 0))
|
||||
|
||||
// positive power: import via meter delta, no export
|
||||
// positive power: energy via meter delta, no return energy
|
||||
clock.Add(3 * time.Minute)
|
||||
require.NoError(t, col.AddEnergy(new(1000.3), nil, 500))
|
||||
require.InDelta(t, 0.3, col.accu.Imported(), 1e-10)
|
||||
require.Equal(t, 0.0, col.accu.Exported())
|
||||
require.InDelta(t, 0.3, col.accu.Energy, 1e-10)
|
||||
require.Equal(t, 0.0, col.accu.ReturnEnergy)
|
||||
|
||||
// negative power: import via meter (no change), export via power integration
|
||||
// negative power: energy via meter (no change), return energy via power integration
|
||||
clock.Add(3 * time.Minute)
|
||||
require.NoError(t, col.AddEnergy(new(1000.3), nil, -600))
|
||||
require.InDelta(t, 0.3, col.accu.Imported(), 1e-10)
|
||||
require.InDelta(t, 600.0*3/60/1e3, col.accu.Exported(), 1e-10) // 0.03 kWh
|
||||
require.InDelta(t, 0.3, col.accu.Energy, 1e-10)
|
||||
require.InDelta(t, 600.0*3/60/1e3, col.accu.ReturnEnergy, 1e-10) // 0.03 kWh
|
||||
}
|
||||
|
||||
func TestCollectorAddEnergyWithExportMeterAndImport(t *testing.T) {
|
||||
func TestCollectorAddEnergyWithReturnEnergyMeterAndEnergy(t *testing.T) {
|
||||
clock := clock.NewMock()
|
||||
|
||||
require.NoError(t, db.NewInstance("sqlite", ":memory:"))
|
||||
|
|
@ -96,21 +96,21 @@ func TestCollectorAddEnergyWithExportMeterAndImport(t *testing.T) {
|
|||
col, err := NewCollector("baz2", "baz2", "", WithClock(clock))
|
||||
require.NoError(t, err)
|
||||
|
||||
// seed export meter
|
||||
// seed return energy meter
|
||||
clock.Add(3 * time.Minute)
|
||||
require.NoError(t, col.AddEnergy(nil, new(1000.0), 0))
|
||||
|
||||
// negative power: export via meter delta, no import
|
||||
// negative power: return energy via meter delta, no energy
|
||||
clock.Add(3 * time.Minute)
|
||||
require.NoError(t, col.AddEnergy(nil, new(1000.3), -500))
|
||||
require.InDelta(t, 0.3, col.accu.Exported(), 1e-10)
|
||||
require.Equal(t, 0.0, col.accu.Imported())
|
||||
require.InDelta(t, 0.3, col.accu.ReturnEnergy, 1e-10)
|
||||
require.Equal(t, 0.0, col.accu.Energy)
|
||||
|
||||
// positive power: export via meter (no change), import via power integration
|
||||
// positive power: return energy via meter (no change), energy via power integration
|
||||
clock.Add(3 * time.Minute)
|
||||
require.NoError(t, col.AddEnergy(nil, new(1000.3), 600))
|
||||
require.InDelta(t, 0.3, col.accu.Exported(), 1e-10)
|
||||
require.InDelta(t, 600.0*3/60/1e3, col.accu.Imported(), 1e-10) // 0.03 kWh
|
||||
require.InDelta(t, 0.3, col.accu.ReturnEnergy, 1e-10)
|
||||
require.InDelta(t, 600.0*3/60/1e3, col.accu.Energy, 1e-10) // 0.03 kWh
|
||||
}
|
||||
|
||||
func TestCollectorAddEnergyWithBothMeters(t *testing.T) {
|
||||
|
|
@ -129,11 +129,11 @@ func TestCollectorAddEnergyWithBothMeters(t *testing.T) {
|
|||
// both deltas used, power ignored
|
||||
clock.Add(3 * time.Minute)
|
||||
require.NoError(t, col.AddEnergy(new(1000.3), new(2000.7), 999))
|
||||
require.InDelta(t, 0.3, col.accu.Imported(), 1e-10)
|
||||
require.InDelta(t, 0.7, col.accu.Exported(), 1e-10)
|
||||
require.InDelta(t, 0.3, col.accu.Energy, 1e-10)
|
||||
require.InDelta(t, 0.7, col.accu.ReturnEnergy, 1e-10)
|
||||
}
|
||||
|
||||
func TestCollectorSetImportAndExportMeterTotal(t *testing.T) {
|
||||
func TestCollectorSetEnergyAndReturnEnergyMeterTotal(t *testing.T) {
|
||||
clock := clock.NewMock()
|
||||
|
||||
require.NoError(t, db.NewInstance("sqlite", ":memory:"))
|
||||
|
|
@ -142,17 +142,17 @@ func TestCollectorSetImportAndExportMeterTotal(t *testing.T) {
|
|||
col, err := NewCollector("set", "set", "", WithClock(clock))
|
||||
require.NoError(t, err)
|
||||
|
||||
// seed both import and export
|
||||
// seed both energy and return energy
|
||||
clock.Add(5 * time.Minute)
|
||||
require.NoError(t, col.SetImportMeterTotal(1000))
|
||||
require.NoError(t, col.SetExportMeterTotal(2000))
|
||||
require.NoError(t, col.SetEnergyMeterTotal(1000))
|
||||
require.NoError(t, col.SetReturnEnergyMeterTotal(2000))
|
||||
|
||||
// both deltas used
|
||||
clock.Add(5 * time.Minute)
|
||||
require.NoError(t, col.SetImportMeterTotal(1000.3))
|
||||
require.NoError(t, col.SetExportMeterTotal(2000.7))
|
||||
require.InDelta(t, 0.3, col.accu.Imported(), 1e-10)
|
||||
require.InDelta(t, 0.7, col.accu.Exported(), 1e-10)
|
||||
require.NoError(t, col.SetEnergyMeterTotal(1000.3))
|
||||
require.NoError(t, col.SetReturnEnergyMeterTotal(2000.7))
|
||||
require.InDelta(t, 0.3, col.accu.Energy, 1e-10)
|
||||
require.InDelta(t, 0.7, col.accu.ReturnEnergy, 1e-10)
|
||||
}
|
||||
|
||||
// TestCollectorSkipsPartialFirstSlot verifies that the first slot, joined
|
||||
|
|
|
|||
|
|
@ -10,9 +10,9 @@ import (
|
|||
|
||||
var ErrIncomplete = errors.New("meter profile incomplete")
|
||||
|
||||
// importProfile returns a 15min average meter profile in Wh. The profile
|
||||
// energyProfile returns a 15min average meter profile in Wh. The profile
|
||||
// is sorted by timestamp starting at 00:00. It is guaranteed to contain 96 15min values.
|
||||
func importProfile(entity entity, from time.Time) (*[96]float64, error) {
|
||||
func energyProfile(entity entity, from time.Time) (*[96]float64, error) {
|
||||
db, err := db.Instance.DB()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
|
|
|||
|
|
@ -201,7 +201,7 @@ func TestUpdateProfile(t *testing.T) {
|
|||
{
|
||||
from := clock.Now().Local().AddDate(0, 0, -2).Add(12 * time.Hour) // 12:00 of day 0
|
||||
|
||||
prof, err := importProfile(entity, from)
|
||||
prof, err := energyProfile(entity, from)
|
||||
require.NoError(t, err)
|
||||
|
||||
var expected [96]float64
|
||||
|
|
@ -219,7 +219,7 @@ func TestUpdateProfile(t *testing.T) {
|
|||
{
|
||||
from := clock.Now().Local().AddDate(0, 0, -3).Add(12 * time.Hour) // 12:00 of day -1
|
||||
|
||||
prof, err := importProfile(entity, from)
|
||||
prof, err := energyProfile(entity, from)
|
||||
require.NoError(t, err)
|
||||
|
||||
var expected [96]float64
|
||||
|
|
|
|||
|
|
@ -552,7 +552,7 @@ func loadpointProfile(lp loadpoint.API, minLen int) []float64 {
|
|||
// homeProfile returns the home base load in Wh
|
||||
func (site *Site) homeProfile(minLen int) ([]float64, error) {
|
||||
// kWh over last 30 days
|
||||
profile, err := site.collectors[metrics.Home].ImportProfile(now.BeginningOfDay().AddDate(0, 0, -30))
|
||||
profile, err := site.collectors[metrics.Home].EnergyProfile(now.BeginningOfDay().AddDate(0, 0, -30))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue