diff --git a/api/api.go b/api/api.go index c9c578979..d17ecb54e 100644 --- a/api/api.go +++ b/api/api.go @@ -2,7 +2,7 @@ package api import "time" -//go:generate mockgen -package mock -destination ../mock/mock_api.go github.com/andig/evcc/api Charger,Meter,MeterEnergy +//go:generate mockgen -package mock -destination ../mock/mock_api.go github.com/andig/evcc/api Charger,Meter,MeterEnergy,Vehicle // ChargeMode are charge modes modeled after OpenWB type ChargeMode string diff --git a/core/loadpoint.go b/core/loadpoint.go index 05c82b86a..da4ba25e0 100644 --- a/core/loadpoint.go +++ b/core/loadpoint.go @@ -542,8 +542,15 @@ func (lp *LoadPoint) remainingChargeDuration(chargePercent float64) time.Duratio } if lp.chargePower > 0 && lp.vehicle != nil { - whTotal := int64(lp.TargetSoC) * lp.vehicle.Capacity() - whRemaining := (1 - chargePercent/100.0) * 1e3 * float64(whTotal) + chargePercent = chargePercent / 100.0 + targetPercent := float64(lp.TargetSoC) / 100 + + if chargePercent >= targetPercent { + return 0 + } + + whTotal := float64(lp.vehicle.Capacity()) * 1e3 + whRemaining := (targetPercent - chargePercent) * whTotal return time.Duration(float64(time.Hour) * whRemaining / lp.chargePower).Round(time.Second) } diff --git a/core/loadpoint_test.go b/core/loadpoint_test.go index e87520e87..5ffa2963e 100644 --- a/core/loadpoint_test.go +++ b/core/loadpoint_test.go @@ -167,33 +167,6 @@ func TestUpdate(t *testing.T) { } } -// func TestConsumedPower(t *testing.T) { -// tc := []struct { -// grid, pv, battery, consumed float64 -// }{ -// {0, 0, 0, 0}, // silent night -// {1, 0, 0, 1}, // grid import -// {0, 1, 0, 1}, // pv sign ignored -// {0, -1, 0, 1}, // pv sign ignored -// {1, 1, 0, 2}, // grid import + pv, pv sign ignored -// {1, -1, 0, 2}, // grid import + pv, pv sign ignored -// {0, 0, 1, 1}, // battery discharging -// {0, 0, -1, -1}, // battery charging -> negative result cannot occur in reality -// {1, -3, 1, 5}, // grid import + pv + battery discharging -// {1, -3, -1, 3}, // grid import + pv + battery charging -> should not happen in reality -// {0, -3, -1, 2}, // pv + battery charging -// {-1, -4, -1, 2}, // grid export + pv + battery charging -// {-1, -4, 0, 3}, // grid export + pv -// } - -// for _, tc := range tc { -// res := consumedPower(tc.pv, tc.battery, tc.grid) -// if res != tc.consumed { -// t.Errorf("consumedPower wanted %.f, got %.f", tc.consumed, res) -// } -// } -// } - func TestPVHysteresisForStatusC(t *testing.T) { dt := time.Minute type se struct { @@ -380,3 +353,23 @@ func TestPVHysteresisForStatusOtherThanC(t *testing.T) { ctrl.Finish() } + +func TestRemainingChargeDuration(t *testing.T) { + lp := NewLoadPoint(util.NewLogger("foo")) + + ctrl := gomock.NewController(t) + vehicle := mock.NewMockVehicle(ctrl) + + lp.vehicle = vehicle + lp.charging = true + + soc := 20.0 + lp.TargetSoC = 80 + lp.chargePower = 1000 + + vehicle.EXPECT().Capacity().Return(int64(10)) + + if remaining := lp.remainingChargeDuration(soc); remaining != 6*time.Hour { + t.Error("wrong remaining charge duration") + } +} diff --git a/mock/mock_api.go b/mock/mock_api.go index 468528ce2..834757a26 100644 --- a/mock/mock_api.go +++ b/mock/mock_api.go @@ -1,5 +1,5 @@ // Code generated by MockGen. DO NOT EDIT. -// Source: github.com/andig/evcc/api (interfaces: Charger,Meter,MeterEnergy) +// Source: github.com/andig/evcc/api (interfaces: Charger,Meter,MeterEnergy,Vehicle) // Package mock is a generated GoMock package. package mock @@ -166,3 +166,69 @@ func (mr *MockMeterEnergyMockRecorder) TotalEnergy() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "TotalEnergy", reflect.TypeOf((*MockMeterEnergy)(nil).TotalEnergy)) } + +// MockVehicle is a mock of Vehicle interface +type MockVehicle struct { + ctrl *gomock.Controller + recorder *MockVehicleMockRecorder +} + +// MockVehicleMockRecorder is the mock recorder for MockVehicle +type MockVehicleMockRecorder struct { + mock *MockVehicle +} + +// NewMockVehicle creates a new mock instance +func NewMockVehicle(ctrl *gomock.Controller) *MockVehicle { + mock := &MockVehicle{ctrl: ctrl} + mock.recorder = &MockVehicleMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use +func (m *MockVehicle) EXPECT() *MockVehicleMockRecorder { + return m.recorder +} + +// Capacity mocks base method +func (m *MockVehicle) Capacity() int64 { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Capacity") + ret0, _ := ret[0].(int64) + return ret0 +} + +// Capacity indicates an expected call of Capacity +func (mr *MockVehicleMockRecorder) Capacity() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Capacity", reflect.TypeOf((*MockVehicle)(nil).Capacity)) +} + +// ChargeState mocks base method +func (m *MockVehicle) ChargeState() (float64, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "ChargeState") + ret0, _ := ret[0].(float64) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// ChargeState indicates an expected call of ChargeState +func (mr *MockVehicleMockRecorder) ChargeState() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ChargeState", reflect.TypeOf((*MockVehicle)(nil).ChargeState)) +} + +// Title mocks base method +func (m *MockVehicle) Title() string { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Title") + ret0, _ := ret[0].(string) + return ret0 +} + +// Title indicates an expected call of Title +func (mr *MockVehicleMockRecorder) Title() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Title", reflect.TypeOf((*MockVehicle)(nil).Title)) +}