Planner: always create simple plan even if tariff errors (#19004)
This commit is contained in:
parent
21cb9bb110
commit
acbcf5461c
6 changed files with 54 additions and 46 deletions
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue