From 96490b49dff2e5483b402a19976f935676b20809 Mon Sep 17 00:00:00 2001 From: andig Date: Fri, 18 Sep 2020 16:43:09 +0200 Subject: [PATCH] Allow charge rater and timer to sum over multiple measurement intervals (#354) --- core/loadpoint.go | 10 +++---- core/wrapper/chargerater.go | 10 ++++--- core/wrapper/chargerater_test.go | 46 +++++++++++++++++++++++--------- core/wrapper/chargetimer.go | 11 +++++--- core/wrapper/chargetimer_test.go | 13 ++++++--- 5 files changed, 61 insertions(+), 29 deletions(-) diff --git a/core/loadpoint.go b/core/loadpoint.go index 9d511b6d3..a9015df14 100644 --- a/core/loadpoint.go +++ b/core/loadpoint.go @@ -222,9 +222,7 @@ func (lp *LoadPoint) configureChargerType(charger api.Charger) { } else { mt := &wrapper.ChargeMeter{} _ = lp.bus.Subscribe(evChargeCurrent, lp.evChargeCurrentWrappedMeterHandler) - _ = lp.bus.Subscribe(evChargeStop, func() { - mt.SetPower(0) - }) + _ = lp.bus.Subscribe(evChargeStop, func() { mt.SetPower(0) }) lp.chargeMeter = mt } } @@ -235,7 +233,8 @@ func (lp *LoadPoint) configureChargerType(charger api.Charger) { } else { rt := wrapper.NewChargeRater(lp.log, lp.chargeMeter) _ = lp.bus.Subscribe(evChargePower, rt.SetChargePower) - _ = lp.bus.Subscribe(evChargeStart, rt.StartCharge) + _ = lp.bus.Subscribe(evVehicleConnect, func() { rt.StartCharge(false) }) + _ = lp.bus.Subscribe(evChargeStart, func() { rt.StartCharge(true) }) _ = lp.bus.Subscribe(evChargeStop, rt.StopCharge) lp.chargeRater = rt } @@ -245,7 +244,8 @@ func (lp *LoadPoint) configureChargerType(charger api.Charger) { lp.chargeTimer = ct } else { ct := wrapper.NewChargeTimer() - _ = lp.bus.Subscribe(evChargeStart, ct.StartCharge) + _ = lp.bus.Subscribe(evVehicleConnect, func() { ct.StartCharge(false) }) + _ = lp.bus.Subscribe(evChargeStart, func() { ct.StartCharge(true) }) _ = lp.bus.Subscribe(evChargeStop, ct.StopCharge) lp.chargeTimer = ct } diff --git a/core/wrapper/chargerater.go b/core/wrapper/chargerater.go index 8c831c45c..12b0d211a 100644 --- a/core/wrapper/chargerater.go +++ b/core/wrapper/chargerater.go @@ -35,7 +35,7 @@ func NewChargeRater(log *util.Logger, meter api.Meter) *ChargeRater { // StartCharge records meter start energy. If meter does not supply TotalEnergy, // start time is recorded and charged energy set to zero. -func (cr *ChargeRater) StartCharge() { +func (cr *ChargeRater) StartCharge(continued bool) { cr.Lock() defer cr.Unlock() @@ -51,7 +51,9 @@ func (cr *ChargeRater) StartCharge() { } else { cr.log.ERROR.Printf("charge meter error %v", err) } - } else { + } + + if !continued { cr.chargedEnergy = 0 } } @@ -67,7 +69,7 @@ func (cr *ChargeRater) StopCharge() { // get end energy amount if m, ok := cr.meter.(api.MeterEnergy); ok { if f, err := m.TotalEnergy(); err == nil { - cr.chargedEnergy = f - cr.startEnergy + cr.chargedEnergy += f - cr.startEnergy cr.log.DEBUG.Printf("final charge energy: %.0fkWh", cr.chargedEnergy) } else { cr.log.ERROR.Printf("charge meter error %v", err) @@ -109,7 +111,7 @@ func (cr *ChargeRater) ChargedEnergy() (float64, error) { f, err := m.TotalEnergy() if err == nil { - return f - cr.startEnergy, nil + return cr.chargedEnergy + f - cr.startEnergy, nil } return 0, fmt.Errorf("charge meter error %v", err) diff --git a/core/wrapper/chargerater_test.go b/core/wrapper/chargerater_test.go index 74e81493a..34f5eb78a 100644 --- a/core/wrapper/chargerater_test.go +++ b/core/wrapper/chargerater_test.go @@ -16,7 +16,7 @@ func TestNoMeter(t *testing.T) { clck := clock.NewMock() cr.clck = clck - cr.StartCharge() + cr.StartCharge(false) // 1kWh clck.Add(time.Hour) @@ -33,12 +33,23 @@ func TestNoMeter(t *testing.T) { clck.Add(time.Hour) cr.SetChargePower(1e3) - f, err := cr.ChargedEnergy() + if f, err := cr.ChargedEnergy(); f != 1 || err != nil { + t.Errorf("energy: %.1f %v", f, err) + } - if f != 1 || err != nil { + // continue + cr.StartCharge(true) + + // 1kWh + clck.Add(2 * time.Hour) + cr.SetChargePower(1e3) + cr.StopCharge() + + if f, err := cr.ChargedEnergy(); f != 3 || err != nil { t.Errorf("energy: %.1f %v", f, err) } } + func TestWrappedMeter(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() @@ -53,18 +64,13 @@ func TestWrappedMeter(t *testing.T) { cm := &EnergyDecorator{Meter: mm, MeterEnergy: me} - me.EXPECT(). - TotalEnergy(). - Return(2.0, nil) - me.EXPECT(). - TotalEnergy(). - Return(3.0, nil) - cr := NewChargeRater(util.NewLogger("foo"), cm) clck := clock.NewMock() cr.clck = clck - cr.StartCharge() + me.EXPECT().TotalEnergy().Return(2.0, nil) + + cr.StartCharge(false) // ignored with meter present clck.Add(time.Hour) @@ -72,15 +78,29 @@ func TestWrappedMeter(t *testing.T) { clck.Add(time.Hour) cr.SetChargePower(0) + me.EXPECT().TotalEnergy().Return(3.0, nil) + cr.StopCharge() + if f, err := cr.ChargedEnergy(); f != 1 || err != nil { + t.Errorf("energy: %.1f %v", f, err) + } + // ignored with meter present clck.Add(time.Hour) cr.SetChargePower(1e3) - f, err := cr.ChargedEnergy() + // continue + me.EXPECT().TotalEnergy().Return(10.0, nil) - if f != 1 || err != nil { + cr.StartCharge(true) + clck.Add(time.Hour) // actual timing ignored as energy comes from meter + + me.EXPECT().TotalEnergy().Return(12.0, nil) + + cr.StopCharge() + + if f, err := cr.ChargedEnergy(); f != 3 || err != nil { t.Errorf("energy: %.1f %v", f, err) } } diff --git a/core/wrapper/chargetimer.go b/core/wrapper/chargetimer.go index a12c62d7a..f713998fb 100644 --- a/core/wrapper/chargetimer.go +++ b/core/wrapper/chargetimer.go @@ -26,13 +26,16 @@ func NewChargeTimer() *ChargeTimer { } // StartCharge signals charge timer start -func (m *ChargeTimer) StartCharge() { +func (m *ChargeTimer) StartCharge(continued bool) { m.Lock() defer m.Unlock() m.charging = true m.start = m.clck.Now() - m.duration = 0 + + if !continued { + m.duration = 0 + } } // StopCharge signals charge timer stop @@ -41,7 +44,7 @@ func (m *ChargeTimer) StopCharge() { defer m.Unlock() m.charging = false - m.duration = m.clck.Since(m.start) + m.duration += m.clck.Since(m.start) } // ChargingTime implements the api.ChargeTimer interface @@ -50,7 +53,7 @@ func (m *ChargeTimer) ChargingTime() (time.Duration, error) { defer m.Unlock() if m.charging { - return m.clck.Since(m.start), nil + return m.duration + m.clck.Since(m.start), nil } return m.duration, nil } diff --git a/core/wrapper/chargetimer_test.go b/core/wrapper/chargetimer_test.go index 99b64fb83..a75192ca1 100644 --- a/core/wrapper/chargetimer_test.go +++ b/core/wrapper/chargetimer_test.go @@ -12,14 +12,21 @@ func TestTimer(t *testing.T) { clck := clock.NewMock() ct.clck = clck - ct.StartCharge() + ct.StartCharge(false) clck.Add(time.Hour) ct.StopCharge() clck.Add(time.Hour) - d, err := ct.ChargingTime() + if d, err := ct.ChargingTime(); d != 1*time.Hour || err != nil { + t.Error(d, err) + } - if d != time.Hour || err != nil { + // continue + ct.StartCharge(true) + clck.Add(2 * time.Hour) + ct.StopCharge() + + if d, err := ct.ChargingTime(); d != 3*time.Hour || err != nil { t.Error(d, err) } }