From 03d4565c0b406c1b37949fa3ed1ee4b5b8786cd5 Mon Sep 17 00:00:00 2001 From: andig Date: Sun, 7 Jun 2026 13:31:23 +0200 Subject: [PATCH] chore: rename metrics import/export to energy/returnEnergy throughout collector and accumulator (#30303) --- core/metrics/accumulator.go | 72 ++++++++++++++------------------ core/metrics/accumulator_test.go | 18 ++++---- core/metrics/collector.go | 46 ++++++++------------ core/metrics/collector_test.go | 68 +++++++++++++++--------------- core/metrics/db_profile.go | 4 +- core/metrics/db_test.go | 4 +- core/site_optimizer.go | 2 +- 7 files changed, 96 insertions(+), 118 deletions(-) diff --git a/core/metrics/accumulator.go b/core/metrics/accumulator.go index 334104d88..aaf3830ec 100644 --- a/core/metrics/accumulator.go +++ b/core/metrics/accumulator.go @@ -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) } } diff --git a/core/metrics/accumulator_test.go b/core/metrics/accumulator_test.go index 62b496214..bd68564ad 100644 --- a/core/metrics/accumulator_test.go +++ b/core/metrics/accumulator_test.go @@ -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) } diff --git a/core/metrics/collector.go b/core/metrics/collector.go index 02abf8c3b..f6249ab2f 100644 --- a/core/metrics/collector.go +++ b/core/metrics/collector.go @@ -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) } diff --git a/core/metrics/collector_test.go b/core/metrics/collector_test.go index 0937d8931..bb80796db 100644 --- a/core/metrics/collector_test.go +++ b/core/metrics/collector_test.go @@ -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 diff --git a/core/metrics/db_profile.go b/core/metrics/db_profile.go index 78d9570ec..664f5a60a 100644 --- a/core/metrics/db_profile.go +++ b/core/metrics/db_profile.go @@ -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 diff --git a/core/metrics/db_test.go b/core/metrics/db_test.go index 7aa6e166e..6ac497335 100644 --- a/core/metrics/db_test.go +++ b/core/metrics/db_test.go @@ -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 diff --git a/core/site_optimizer.go b/core/site_optimizer.go index d0f6edaf5..93439fbea 100644 --- a/core/site_optimizer.go +++ b/core/site_optimizer.go @@ -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 }