diff --git a/core/loadpoint/api.go b/core/loadpoint/api.go index 25a95bbb8..83b6ef9d4 100644 --- a/core/loadpoint/api.go +++ b/core/loadpoint/api.go @@ -116,7 +116,7 @@ type API interface { // SocBasedPlanning determines if the planner is soc based SocBasedPlanning() bool // GetPlan creates a charging plan - GetPlan(targetTime time.Time, requiredDuration time.Duration) (api.Rates, error) + GetPlan(targetTime time.Time, requiredDuration time.Duration) api.Rates // GetSocConfig returns the soc poll settings GetSocConfig() SocConfig diff --git a/core/loadpoint/mock.go b/core/loadpoint/mock.go index 0f150589d..620bdc20d 100644 --- a/core/loadpoint/mock.go +++ b/core/loadpoint/mock.go @@ -420,12 +420,11 @@ func (mr *MockAPIMockRecorder) GetPhasesConfigured() *gomock.Call { } // GetPlan mocks base method. -func (m *MockAPI) GetPlan(targetTime time.Time, requiredDuration time.Duration) (api.Rates, error) { +func (m *MockAPI) GetPlan(targetTime time.Time, requiredDuration time.Duration) api.Rates { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetPlan", targetTime, requiredDuration) ret0, _ := ret[0].(api.Rates) - ret1, _ := ret[1].(error) - return ret0, ret1 + return ret0 } // GetPlan indicates an expected call of GetPlan. diff --git a/core/loadpoint_plan.go b/core/loadpoint_plan.go index 60f6acf92..064990ae0 100644 --- a/core/loadpoint_plan.go +++ b/core/loadpoint_plan.go @@ -79,9 +79,9 @@ func (lp *Loadpoint) GetPlanGoal() (float64, bool) { } // GetPlan creates a charging plan for given time and duration -func (lp *Loadpoint) GetPlan(targetTime time.Time, requiredDuration time.Duration) (api.Rates, error) { +func (lp *Loadpoint) GetPlan(targetTime time.Time, requiredDuration time.Duration) api.Rates { if lp.planner == nil || targetTime.IsZero() { - return nil, nil + return nil } return lp.planner.Plan(requiredDuration, targetTime) @@ -132,9 +132,8 @@ func (lp *Loadpoint) plannerActive() (active bool) { return false } - plan, err := lp.GetPlan(planTime, requiredDuration) - if err != nil { - lp.log.ERROR.Println("planner:", err) + plan := lp.GetPlan(planTime, requiredDuration) + if plan == nil { return false } diff --git a/core/planner/planner.go b/core/planner/planner.go index a9e3301f0..cfa976057 100644 --- a/core/planner/planner.go +++ b/core/planner/planner.go @@ -136,9 +136,9 @@ func (t *Planner) continuousPlan(rates api.Rates, start, end time.Time) api.Rate return res } -func (t *Planner) Plan(requiredDuration time.Duration, targetTime time.Time) (api.Rates, error) { +func (t *Planner) Plan(requiredDuration time.Duration, targetTime time.Time) api.Rates { if t == nil || requiredDuration <= 0 { - return nil, nil + return nil } latestStart := targetTime.Add(-requiredDuration) @@ -157,19 +157,19 @@ func (t *Planner) Plan(requiredDuration time.Duration, targetTime time.Time) (ap // target charging without tariff or late start if t.tariff == nil { - return simplePlan, nil + return simplePlan } rates, err := t.tariff.Rates() // treat like normal target charging if we don't have rates if len(rates) == 0 || err != nil { - return simplePlan, err + return simplePlan } // consume remaining time if t.clock.Until(targetTime) <= requiredDuration { - return t.continuousPlan(rates, latestStart, targetTime), nil + return t.continuousPlan(rates, latestStart, targetTime) } // rates are by default sorted by date, oldest to newest @@ -183,7 +183,7 @@ func (t *Planner) Plan(requiredDuration time.Duration, targetTime time.Time) (ap // there is enough time for charging after end of current rates durationAfterRates := targetTime.Sub(last) if durationAfterRates >= requiredDuration { - return nil, nil + return nil } // need to use some of the available slots @@ -199,5 +199,5 @@ func (t *Planner) Plan(requiredDuration time.Duration, targetTime time.Time) (ap // sort plan by time plan.Sort() - return plan, nil + return plan } diff --git a/core/planner/planner_test.go b/core/planner/planner_test.go index e767447ba..e68972225 100644 --- a/core/planner/planner_test.go +++ b/core/planner/planner_test.go @@ -133,8 +133,29 @@ func TestNilTariff(t *testing.T) { clock: clock, } - plan, err := p.Plan(time.Hour, clock.Now().Add(30*time.Minute)) - require.NoError(t, err) + plan := p.Plan(time.Hour, clock.Now().Add(30*time.Minute)) + assert.Equal(t, api.Rates{ + { + Start: clock.Now(), + End: clock.Now().Add(60 * time.Minute), + }, + }, plan, "expected simple plan") +} + +func TestRatesError(t *testing.T) { + clock := clock.NewMock() + ctrl := gomock.NewController(t) + + trf := api.NewMockTariff(ctrl) + trf.EXPECT().Rates().AnyTimes().Return(nil, api.ErrOutdated) + + p := &Planner{ + log: util.NewLogger("foo"), + clock: clock, + tariff: trf, + } + + plan := p.Plan(time.Hour, clock.Now().Add(30*time.Minute)) assert.Equal(t, api.Rates{ { Start: clock.Now(), @@ -163,12 +184,10 @@ func TestFlatTariffTargetInThePast(t *testing.T) { }, } - plan, err := p.Plan(time.Hour, clock.Now().Add(30*time.Minute)) - require.NoError(t, err) + plan := p.Plan(time.Hour, clock.Now().Add(30*time.Minute)) assert.Equal(t, simplePlan, plan, "expected simple plan") - plan, err = p.Plan(time.Hour, clock.Now().Add(-30*time.Minute)) - require.NoError(t, err) + plan = p.Plan(time.Hour, clock.Now().Add(-30*time.Minute)) assert.Equal(t, simplePlan, plan, "expected simple plan") } @@ -189,14 +208,12 @@ func TestFlatTariffLongSlots(t *testing.T) { // that slots are not longer than 1 hour and with that context this is not a problem // expect 00:00-01:00 UTC - plan, err := p.Plan(time.Hour, clock.Now().Add(2*time.Hour)) - require.NoError(t, err) + plan := p.Plan(time.Hour, clock.Now().Add(2*time.Hour)) assert.Equal(t, api.Rate{Start: clock.Now(), End: clock.Now().Add(time.Hour)}, SlotAt(clock.Now(), plan)) assert.Equal(t, api.Rate{}, SlotAt(clock.Now().Add(time.Hour), plan)) // expect 00:00-01:00 UTC - plan, err = p.Plan(time.Hour, clock.Now().Add(time.Hour)) - require.NoError(t, err) + plan = p.Plan(time.Hour, clock.Now().Add(time.Hour)) assert.Equal(t, api.Rate{Start: clock.Now(), End: clock.Now().Add(time.Hour)}, SlotAt(clock.Now(), plan)) } @@ -213,12 +230,10 @@ func TestTargetAfterKnownPrices(t *testing.T) { tariff: trf, } - plan, err := p.Plan(40*time.Minute, clock.Now().Add(2*time.Hour)) // charge efficiency does not allow to test with 1h - require.NoError(t, err) + plan := p.Plan(40*time.Minute, clock.Now().Add(2*time.Hour)) // charge efficiency does not allow to test with 1h assert.False(t, !SlotAt(clock.Now(), plan).IsZero(), "should not start if car can be charged completely after known prices ") - plan, err = p.Plan(2*time.Hour, clock.Now().Add(2*time.Hour)) - require.NoError(t, err) + plan = p.Plan(2*time.Hour, clock.Now().Add(2*time.Hour)) assert.True(t, !SlotAt(clock.Now(), plan).IsZero(), "should start if car can not be charged completely after known prices ") } @@ -242,12 +257,10 @@ func TestChargeAfterTargetTime(t *testing.T) { }, } - plan, err := p.Plan(time.Hour, clock.Now()) - require.NoError(t, err) + plan := p.Plan(time.Hour, clock.Now()) assert.Equal(t, simplePlan, plan, "expected simple plan") - plan, err = p.Plan(time.Hour, clock.Now().Add(-time.Hour)) - require.NoError(t, err) + plan = p.Plan(time.Hour, clock.Now().Add(-time.Hour)) assert.Equal(t, simplePlan, plan, "expected simple plan") } @@ -259,8 +272,7 @@ func TestContinuousPlanNoTariff(t *testing.T) { clock: clock, } - plan, err := p.Plan(time.Hour, clock.Now()) - require.NoError(t, err) + plan := p.Plan(time.Hour, clock.Now()) // single-slot plan assert.Len(t, plan, 1) @@ -281,8 +293,7 @@ func TestContinuousPlan(t *testing.T) { tariff: trf, } - plan, err := p.Plan(150*time.Minute, clock.Now()) - require.NoError(t, err) + plan := p.Plan(150*time.Minute, clock.Now()) // 3-slot plan assert.Len(t, plan, 3) @@ -301,8 +312,7 @@ func TestContinuousPlanOutsideRates(t *testing.T) { tariff: trf, } - plan, err := p.Plan(30*time.Minute, clock.Now()) - require.NoError(t, err) + plan := p.Plan(30*time.Minute, clock.Now()) // 3-slot plan assert.Len(t, plan, 1) diff --git a/server/http_loadpoint_handler.go b/server/http_loadpoint_handler.go index 632581873..a7def3467 100644 --- a/server/http_loadpoint_handler.go +++ b/server/http_loadpoint_handler.go @@ -50,8 +50,8 @@ func planHandler(lp loadpoint.API) http.HandlerFunc { goal, _ := lp.GetPlanGoal() requiredDuration := lp.GetPlanRequiredDuration(goal, maxPower) - plan, err := lp.GetPlan(planTime, requiredDuration) - if err != nil { + plan := lp.GetPlan(planTime, requiredDuration) + if plan == nil { w.WriteHeader(http.StatusBadRequest) return } @@ -109,8 +109,8 @@ func staticPlanPreviewHandler(lp loadpoint.API) http.HandlerFunc { maxPower := lp.EffectiveMaxPower() requiredDuration := lp.GetPlanRequiredDuration(goal, maxPower) - plan, err := lp.GetPlan(planTime, requiredDuration) - if err != nil { + plan := lp.GetPlan(planTime, requiredDuration) + if plan == nil { w.WriteHeader(http.StatusBadRequest) return } @@ -162,8 +162,8 @@ func repeatingPlanPreviewHandler(lp loadpoint.API) http.HandlerFunc { maxPower := lp.EffectiveMaxPower() requiredDuration := lp.GetPlanRequiredDuration(soc, maxPower) - plan, err := lp.GetPlan(planTime, requiredDuration) - if err != nil { + plan := lp.GetPlan(planTime, requiredDuration) + if plan == nil { w.WriteHeader(http.StatusBadRequest) return }