Planner: improve plan selection (#18211)
This commit is contained in:
parent
81701912f8
commit
285b22ee6e
3 changed files with 69 additions and 16 deletions
|
|
@ -1,7 +1,7 @@
|
|||
package core
|
||||
|
||||
import (
|
||||
"sort"
|
||||
"slices"
|
||||
"time"
|
||||
|
||||
"github.com/evcc-io/evcc/api"
|
||||
|
|
@ -31,20 +31,39 @@ func (lp *Loadpoint) EffectivePriority() int {
|
|||
return lp.GetPriority()
|
||||
}
|
||||
|
||||
type plan struct {
|
||||
Id int
|
||||
Start time.Time // last possible start time
|
||||
End time.Time // user-selected finish time
|
||||
Soc int
|
||||
}
|
||||
|
||||
func (lp *Loadpoint) nextActivePlan(maxPower float64, plans []plan) *plan {
|
||||
for i, p := range plans {
|
||||
requiredDuration := lp.GetPlanRequiredDuration(float64(p.Soc), maxPower)
|
||||
plans[i].Start = p.End.Add(-requiredDuration)
|
||||
}
|
||||
|
||||
// sort plans by start time
|
||||
slices.SortStableFunc(plans, func(i, j plan) int {
|
||||
return i.Start.Compare(j.Start)
|
||||
})
|
||||
|
||||
if len(plans) > 0 {
|
||||
return &plans[0]
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// nextVehiclePlan returns the next vehicle plan time, soc and id
|
||||
func (lp *Loadpoint) nextVehiclePlan() (time.Time, int, int) {
|
||||
if v := lp.GetVehicle(); v != nil {
|
||||
type plan struct {
|
||||
Time time.Time
|
||||
Soc int
|
||||
Id int
|
||||
}
|
||||
|
||||
var plans []plan
|
||||
|
||||
// static plan
|
||||
if planTime, soc := vehicle.Settings(lp.log, v).GetPlanSoc(); soc != 0 {
|
||||
plans = append(plans, plan{Id: 1, Soc: soc, Time: planTime})
|
||||
plans = append(plans, plan{Id: 1, Soc: soc, End: planTime})
|
||||
}
|
||||
|
||||
// repeating plans
|
||||
|
|
@ -59,16 +78,12 @@ func (lp *Loadpoint) nextVehiclePlan() (time.Time, int, int) {
|
|||
continue
|
||||
}
|
||||
|
||||
plans = append(plans, plan{Id: index + 2, Soc: rp.Soc, Time: time})
|
||||
plans = append(plans, plan{Id: index + 2, Soc: rp.Soc, End: time})
|
||||
}
|
||||
|
||||
// sort plans by time
|
||||
sort.Slice(plans, func(i, j int) bool {
|
||||
return plans[i].Time.Before(plans[j].Time)
|
||||
})
|
||||
|
||||
if len(plans) > 0 {
|
||||
return plans[0].Time, plans[0].Soc, plans[0].Id
|
||||
// calculate earliest required plan start
|
||||
if plan := lp.nextActivePlan(lp.EffectiveMaxPower(), plans); plan != nil {
|
||||
return plan.End, plan.Soc, plan.Id
|
||||
}
|
||||
}
|
||||
return time.Time{}, 0, 0
|
||||
|
|
|
|||
|
|
@ -2,10 +2,13 @@ package core
|
|||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/benbjohnson/clock"
|
||||
"github.com/evcc-io/evcc/api"
|
||||
"github.com/evcc-io/evcc/util"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
"go.uber.org/mock/gomock"
|
||||
)
|
||||
|
||||
|
|
@ -68,3 +71,37 @@ func TestEffectiveMinMaxCurrent(t *testing.T) {
|
|||
assert.Equal(t, tc.effectiveMax, lp.effectiveMaxCurrent(), "max")
|
||||
}
|
||||
}
|
||||
|
||||
func TestNextPlan(t *testing.T) {
|
||||
clock := clock.NewMock()
|
||||
|
||||
ctrl := gomock.NewController(t)
|
||||
lp := NewLoadpoint(util.NewLogger("foo"), nil)
|
||||
lp.charger = api.NewMockCharger(ctrl)
|
||||
|
||||
for _, tc := range []struct {
|
||||
planId int
|
||||
plans []plan
|
||||
}{
|
||||
{1, []plan{
|
||||
{Id: 1, End: clock.Now().Add(8 * time.Hour), Soc: 10},
|
||||
{Id: 2, End: clock.Now().Add(10 * time.Hour), Soc: 10},
|
||||
}},
|
||||
{1, []plan{
|
||||
{Id: 1, End: clock.Now().Add(8 * time.Hour), Soc: 20},
|
||||
{Id: 2, End: clock.Now().Add(9 * time.Hour), Soc: 20},
|
||||
}},
|
||||
{2, []plan{
|
||||
{Id: 2, End: clock.Now().Add(8 * time.Hour), Soc: 20},
|
||||
{Id: 1, End: clock.Now().Add(9 * time.Hour), Soc: 20},
|
||||
}},
|
||||
{2, []plan{
|
||||
{Id: 1, End: clock.Now().Add(8 * time.Hour), Soc: 10},
|
||||
{Id: 2, End: clock.Now().Add(10 * time.Hour), Soc: 60},
|
||||
}},
|
||||
} {
|
||||
res := lp.nextActivePlan(1e4, tc.plans)
|
||||
require.NotNil(t, res)
|
||||
assert.Equal(t, tc.planId, res.Id)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -497,6 +497,7 @@ test.describe("repeating", async () => {
|
|||
const plan1 = modal.getByTestId("plan-entry").nth(0);
|
||||
await plan1.getByTestId("static-plan-day").selectOption({ index: 1 });
|
||||
await plan1.getByTestId("static-plan-time").fill("09:30");
|
||||
await plan1.getByTestId("static-plan-soc").selectOption("80%");
|
||||
await plan1.getByTestId("static-plan-active").click();
|
||||
|
||||
// add repeating plan for tomorrow
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue