Skip to content

Commit

Permalink
Only ever start first slot late if not last slot
Browse files Browse the repository at this point in the history
  • Loading branch information
andig committed Apr 2, 2023
1 parent 78095be commit f81d8cc
Show file tree
Hide file tree
Showing 5 changed files with 101 additions and 44 deletions.
22 changes: 13 additions & 9 deletions core/planner/helper.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,20 +46,24 @@ func SlotAt(time time.Time, plan api.Rates) api.Rate {
return api.Rate{}
}

func SlotHasPredecessor(r api.Rate, plan api.Rates) bool {
for i, slot := range plan {
if r.Start.Equal(slot.Start) && r.End.Equal(slot.End) && i > 0 {
return plan[i-1].End.Equal(r.Start)
// SlotHasSuccessor returns if the slot has an immediate successor.
// Does not require the plan to be sorted by start time.
func SlotHasSuccessor(r api.Rate, plan api.Rates) bool {
for _, slot := range plan {
if r.End.Equal(slot.Start) {
return true
}
}
return false
}

func SlotHasSuccessor(r api.Rate, plan api.Rates) bool {
for i, slot := range plan {
if r.Start.Equal(slot.Start) && r.End.Equal(slot.End) && len(plan) > i+1 {
return plan[i+1].Start.Equal(r.End)
// IsFirst returns if the slot is the first slot in the plan.
// Does not require the plan to be sorted by start time.
func IsFirst(r api.Rate, plan api.Rates) bool {
for _, slot := range plan {
if r.Start.After(slot.Start) {
return false
}
}
return false
return true
}
43 changes: 34 additions & 9 deletions core/planner/helper_test.go
Original file line number Diff line number Diff line change
@@ -1,24 +1,49 @@
package planner

import (
"math/rand"
"testing"
"time"

"github.com/benbjohnson/clock"
"github.com/evcc-io/evcc/api"
"github.com/stretchr/testify/require"
)

func TestSlotHasPreDecessor(t *testing.T) {
func TestSlotHasSuccessor(t *testing.T) {
plan := rates([]float64{20, 60, 10, 80, 40, 90}, time.Now(), time.Hour)
for i := 1; i < len(plan); i++ {
require.True(t, SlotHasPredecessor(plan[i], plan))

last := plan[len(plan)-1]
rand.Shuffle(len(plan)-1, func(i, j int) {
plan[i], plan[j] = plan[j], plan[i]
})

for i := 0; i < len(plan); i++ {
if plan[i] != last {
require.True(t, SlotHasSuccessor(plan[i], plan))
}
}
require.False(t, SlotHasPredecessor(plan[0], plan))

require.False(t, SlotHasSuccessor(last, plan))
}

func TestSlotHasSuccessor(t *testing.T) {
plan := rates([]float64{20, 60, 10, 80, 40, 90}, time.Now(), time.Hour)
for i := 0; i < len(plan)-1; i++ {
require.True(t, SlotHasSuccessor(plan[i], plan))
func TestIsFirst(t *testing.T) {
clock := clock.NewMock()
plan := rates([]float64{20, 60, 10, 80, 40, 90}, clock.Now(), time.Hour)

first := plan[0]
rand.Shuffle(len(plan), func(i, j int) {
plan[i], plan[j] = plan[j], plan[i]
})

for i := 1; i < len(plan); i++ {
if plan[i] != first {
require.False(t, IsFirst(plan[i], plan))
}
}
require.False(t, SlotHasSuccessor(plan[len(plan)-1], plan))

require.True(t, IsFirst(first, plan))

// ensure single slot is always first
require.True(t, IsFirst(first, []api.Rate{first}))
}
7 changes: 4 additions & 3 deletions core/planner/planner.go
Original file line number Diff line number Diff line change
Expand Up @@ -57,10 +57,11 @@ func (t *Planner) plan(rates api.Rates, requiredDuration time.Duration, targetTi

// slot covers more than we need, so shorten it
if requiredDuration < 0 {
if SlotHasPredecessor(slot, plan) || !SlotHasSuccessor(slot, plan) {
slot.End = slot.End.Add(requiredDuration)
} else {
// the first (if not single) slot should start as late as possible
if IsFirst(slot, plan) && len(plan) > 0 {
slot.Start = slot.Start.Add(-requiredDuration)
} else {
slot.End = slot.End.Add(requiredDuration)
}
requiredDuration = 0

Expand Down
6 changes: 4 additions & 2 deletions core/planner/planner_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -81,11 +81,11 @@ func TestPlan(t *testing.T) {
30,
},
{
"plan 30(30)-0-60-0-0-0",
"plan (30)30-0-60-0-0-0",
time.Duration(90 * time.Minute),
clock.Now(),
clock.Now().Add(6 * time.Hour),
clock.Now(),
clock.Now().Add(30 * time.Minute),
20,
},
{
Expand Down Expand Up @@ -177,6 +177,8 @@ func TestFlatTariffLongSlots(t *testing.T) {
tariff: trf,
}

// for a single slot, we always expect charging to start early

// expect 00:00-01:00 UTC
plan, err := p.Plan(time.Hour, clock.Now().Add(2*time.Hour))
assert.NoError(t, err)
Expand Down
67 changes: 46 additions & 21 deletions tariff/fixed_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,14 +19,16 @@ func TestFixed(t *testing.T) {
}

var expect api.Rates
for i := 0; i < 7; i++ {
dayStart := carbon.FromStdTime(tf.clock.Now()).StartOfDay().AddDays(i)
for dow := 0; dow < 7; dow++ {
dayStart := carbon.FromStdTime(tf.clock.Now()).StartOfDay().AddDays(dow)

expect = append(expect, api.Rate{
Price: 0.3,
Start: dayStart.ToStdTime(),
End: dayStart.AddDay().ToStdTime(),
})
for hour := 0; hour < 24; hour++ {
expect = append(expect, api.Rate{
Price: 0.3,
Start: dayStart.AddHours(hour).ToStdTime(),
End: dayStart.AddHours(hour + 1).ToStdTime(),
})
}
}

rates, err := tf.Rates()
Expand All @@ -53,23 +55,46 @@ func TestFixedSplitZones(t *testing.T) {
for i := 0; i < 7; i++ {
dayStart := carbon.FromStdTime(tf.clock.Now()).StartOfDay().AddDays(i)

expect = append(expect,
api.Rate{
// 00:00-05:00 0.1
for hour := 0; hour < 5; hour++ {
expect = append(expect, api.Rate{
Price: 0.1,
Start: dayStart.ToStdTime(),
End: dayStart.AddHours(5).AddMinutes(30).ToStdTime(),
},
api.Rate{
Start: dayStart.AddHours(hour).ToStdTime(),
End: dayStart.AddHours(hour + 1).ToStdTime(),
})
}

// 05:00-05:30 0.1
expect = append(expect, api.Rate{
Price: 0.1,
Start: dayStart.AddHours(5).ToStdTime(),
End: dayStart.AddHours(5).AddMinutes(30).ToStdTime(),
})

// 05:30-06:00 0.5
expect = append(expect, api.Rate{
Price: 0.5,
Start: dayStart.AddHours(5).AddMinutes(30).ToStdTime(),
End: dayStart.AddHours(6).ToStdTime(),
})

// 06:00-21:00 0.5
for hour := 6; hour < 21; hour++ {
expect = append(expect, api.Rate{
Price: 0.5,
Start: dayStart.AddHours(5).AddMinutes(30).ToStdTime(),
End: dayStart.AddHours(21).ToStdTime(),
},
api.Rate{
Start: dayStart.AddHours(hour).ToStdTime(),
End: dayStart.AddHours(hour + 1).ToStdTime(),
})
}

// 21:00-00:00 0.1
for hour := 21; hour < 24; hour++ {
expect = append(expect, api.Rate{
Price: 0.1,
Start: dayStart.AddHours(21).ToStdTime(),
End: dayStart.AddDay().ToStdTime(),
},
)
Start: dayStart.AddHours(hour).ToStdTime(),
End: dayStart.AddHours(hour + 1).ToStdTime(),
})
}
}

rates, err := tf.Rates()
Expand Down

0 comments on commit f81d8cc

Please sign in to comment.