From 446a7c440842e68ad1dae72c370c0610517b250d Mon Sep 17 00:00:00 2001 From: andig Date: Sat, 11 Feb 2023 13:24:35 +0100 Subject: [PATCH] Sessions: use meter energy reading to correct session charged energy (#6197) --- core/db/db.go | 24 ++++++++-- core/loadpoint_session.go | 11 +++-- core/loadpoint_session_test.go | 87 ++++++++++++++++++++++++++++++++++ core/site.go | 15 ------ 4 files changed, 115 insertions(+), 22 deletions(-) create mode 100644 core/loadpoint_session_test.go diff --git a/core/db/db.go b/core/db/db.go index 8d38ebee4..692ae0012 100644 --- a/core/db/db.go +++ b/core/db/db.go @@ -20,13 +20,24 @@ type Database interface { // New creates a database storage driver func New(name string) (*DB, error) { - db := &DB{ + db := serverdb.Instance + + // TODO deprecate + var err error + if table := "transactions"; db.Migrator().HasTable(table) { + err = db.Migrator().RenameTable(table, new(Session)) + } + if err == nil { + err = db.AutoMigrate(new(Session)) + } + + sessiondb := &DB{ log: util.NewLogger("db"), - db: serverdb.Instance, + db: db, name: name, } - return db, nil + return sessiondb, err } // Session creates a charging session @@ -45,3 +56,10 @@ func (s *DB) Persist(session interface{}) { s.log.ERROR.Printf("persist: %v", err) } } + +// Return sessions +func (s *DB) Sessions() (Sessions, error) { + var res Sessions + tx := s.db.Find(&res) + return res, tx.Error +} diff --git a/core/loadpoint_session.go b/core/loadpoint_session.go index 91f52257f..b5a054d8b 100644 --- a/core/loadpoint_session.go +++ b/core/loadpoint_session.go @@ -1,8 +1,6 @@ package core import ( - "time" - "github.com/evcc-io/evcc/api" "github.com/evcc-io/evcc/core/db" ) @@ -55,10 +53,15 @@ func (lp *Loadpoint) stopSession() { return } - lp.session.Finished = time.Now() + lp.session.Finished = lp.clock.Now() lp.session.MeterStop = lp.chargeMeterTotal() - if chargedEnergy := lp.getChargedEnergy() / 1e3; chargedEnergy > lp.session.ChargedEnergy { + chargedEnergy := lp.getChargedEnergy() / 1e3 + if delta := lp.session.MeterStop - lp.session.MeterStart; delta < chargedEnergy && lp.session.MeterStart*lp.session.MeterStop > 0 { + chargedEnergy = delta + } + + if chargedEnergy > lp.session.ChargedEnergy { lp.session.ChargedEnergy = chargedEnergy } diff --git a/core/loadpoint_session_test.go b/core/loadpoint_session_test.go new file mode 100644 index 000000000..95d624940 --- /dev/null +++ b/core/loadpoint_session_test.go @@ -0,0 +1,87 @@ +package core + +import ( + "testing" + "time" + + "github.com/benbjohnson/clock" + "github.com/evcc-io/evcc/api" + coredb "github.com/evcc-io/evcc/core/db" + "github.com/evcc-io/evcc/mock" + serverdb "github.com/evcc-io/evcc/server/db" + "github.com/golang/mock/gomock" + "github.com/stretchr/testify/assert" +) + +func TestSession(t *testing.T) { + var err error + serverdb.Instance, err = serverdb.New("sqlite", ":memory:") + assert.NoError(t, err) + + db, err := coredb.New("foo") + assert.NoError(t, err) + + clock := clock.NewMock() + + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + mm := mock.NewMockMeter(ctrl) + me := mock.NewMockMeterEnergy(ctrl) + + type EnergyDecorator struct { + api.Meter + api.MeterEnergy + } + + cm := &EnergyDecorator{Meter: mm, MeterEnergy: me} + + lp := &Loadpoint{ + clock: clock, + db: db, + chargeMeter: cm, + } + + // create session + me.EXPECT().TotalEnergy().Return(1.0, nil) + lp.createSession() + assert.NotNil(t, lp.session) + + // start charging + lp.updateSession(func(session *coredb.Session) { + if session.Created.IsZero() { + session.Created = lp.clock.Now() + } + }) + assert.Equal(t, clock.Now(), lp.session.Created) + + // stop charging + clock.Add(time.Hour) + lp.chargedEnergy = 1.23 * 1e3 // Wh + me.EXPECT().TotalEnergy().Return(1.0+lp.chargedEnergy/1e3, nil) // match chargedEnergy + + lp.stopSession() + assert.NotNil(t, lp.session) + assert.Equal(t, lp.chargedEnergy/1e3, lp.session.ChargedEnergy) + assert.Equal(t, clock.Now(), lp.session.Finished) + + s, err := db.Sessions() + assert.NoError(t, err) + assert.Len(t, s, 1) + t.Log(s) + + // stop charging - 2nd leg + clock.Add(time.Hour) + lp.chargedEnergy *= 2 + me.EXPECT().TotalEnergy().Return(3.0, nil) // doesn't match chargedEnergy + + lp.stopSession() + assert.NotNil(t, lp.session) + assert.Equal(t, 3.0-1.0, lp.session.ChargedEnergy) // expect actual meter energy delta + assert.Equal(t, clock.Now(), lp.session.Finished) + + s, err = db.Sessions() + assert.NoError(t, err) + assert.Len(t, s, 1) + t.Log(s) +} diff --git a/core/site.go b/core/site.go index b8f6262b4..95ba444ad 100644 --- a/core/site.go +++ b/core/site.go @@ -110,21 +110,6 @@ func NewSiteFromConfig( site.coordinator = coordinator.New(log, vehicles) site.savings = NewSavings(tariffs) - // migrate session log - if serverdb.Instance != nil { - var err error - // TODO deprecate - if table := "transactions"; serverdb.Instance.Migrator().HasTable(table) { - err = serverdb.Instance.Migrator().RenameTable(table, new(db.Session)) - } - if err == nil { - err = serverdb.Instance.AutoMigrate(new(db.Session)) - } - if err != nil { - return nil, err - } - } - // upload telemetry on shutdown if telemetry.Enabled() { shutdown.Register(func() {