Allow charge rater and timer to sum over multiple measurement intervals (#354)

This commit is contained in:
andig 2020-09-18 16:43:09 +02:00 • committed by GitHub
parent c78a43b340
commit 96490b49df
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 61 additions and 29 deletions

View file

@ -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
}

View file

@ -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)

View file

@ -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)
}
}

View file

@ -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
}

View file

@ -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)
}
}