Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature/Implement logic to construct charge graph #203

Closed
wants to merge 16 commits into from
Closed
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions integration/oasis/chargingstrategy/charge_strategy_creator.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,16 @@ type ChargingStrategy struct {
ChargingEnergy float64
}

type ChargingCost struct {
Duration float64
// money, etc
}

// ChargingStrategyCreator defines interface related with creation of charging strategy
type ChargingStrategyCreator interface {

// CreateChargingStrategies creates charge strategies which could be used by other algorithm
CreateChargingStrategies() []ChargingStrategy

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Create charging candidates

EvaluateCost(arrivalEnergy float64, targetState ChargingStrategy) ChargingCost
}
87 changes: 77 additions & 10 deletions integration/oasis/chargingstrategy/chargingstrategy.go
Original file line number Diff line number Diff line change
@@ -1,34 +1,101 @@
package chargingstrategy

import (
"github.com/golang/glog"
)

type fakeChargingStrategyCreator struct {
maxEnergyLevel float64
arrivalEnergyLevel float64
maxEnergyLevel float64
}

// NewFakeChargingStrategyCreator creates fake charging strategy
func NewFakeChargingStrategyCreator(maxEnergyLevel float64) *fakeChargingStrategyCreator {
func NewFakeChargingStrategyCreator(arrivalEnergyLevel, maxEnergyLevel float64) *fakeChargingStrategyCreator {
return &fakeChargingStrategyCreator{
maxEnergyLevel: maxEnergyLevel,
arrivalEnergyLevel: arrivalEnergyLevel,
maxEnergyLevel: maxEnergyLevel,
}
}

// @todo:
// - Influence of returning candidate with no charge time and additional energy
// CreateChargingStrategies returns different charging strategy
// Initial implementation: 1 hour charge for 60% of max energy,
// 2 hour charge for 80%
// 4 hour charge for 100%
func (f *fakeChargingStrategyCreator) CreateChargingStrategies() []ChargingStrategy {

return []ChargingStrategy{
ChargingStrategy{
ChargingTime: 3600,
ChargingEnergy: f.maxEnergyLevel * 0.6,
},
ChargingStrategy{
ChargingTime: 7200,
ChargingEnergy: f.maxEnergyLevel * 0.8,
},
ChargingStrategy{
ChargingTime: 14400,
ChargingEnergy: f.maxEnergyLevel,
},
}
}

// Fake charge strategy
// From empty energy:
// 1 hour charge to 60% of max energy
// 2 hour charge to 80%, means from 60% ~ 80% need 1 hour
// 4 hour charge to 100%, means from 80% ~ 100% need 2 hours
func (f *fakeChargingStrategyCreator) EvaluateCost(arrivalEnergy float64, targetState ChargingStrategy) ChargingCost {
sixtyPercentOfMaxEnergy := f.maxEnergyLevel * 0.6
eightyPercentOfMaxEnergy := f.maxEnergyLevel * 0.8
noNeedCharge := ChargingCost{
Duration: 0.0,
}

if arrivalEnergy > targetState.ChargingEnergy ||
floatEquals(targetState.ChargingEnergy, 0.0) {
return noNeedCharge
}

totalTime := 0.0
currentEnergy := arrivalEnergy
if arrivalEnergy < sixtyPercentOfMaxEnergy {
energyNeeded4Stage1 := sixtyPercentOfMaxEnergy - arrivalEnergy
totalTime += energyNeeded4Stage1 / sixtyPercentOfMaxEnergy * 3600.0
currentEnergy = sixtyPercentOfMaxEnergy
}

if floatEquals(targetState.ChargingEnergy, sixtyPercentOfMaxEnergy) {
return ChargingCost{
Duration: totalTime,
}
}

if arrivalEnergy < eightyPercentOfMaxEnergy {
energyNeeded4Stage2 := eightyPercentOfMaxEnergy - currentEnergy
totalTime += energyNeeded4Stage2 / (eightyPercentOfMaxEnergy - sixtyPercentOfMaxEnergy) * 3600.0
currentEnergy = eightyPercentOfMaxEnergy
}
if floatEquals(targetState.ChargingEnergy, eightyPercentOfMaxEnergy) {
return ChargingCost{
Duration: totalTime,
}
}

if arrivalEnergy < f.maxEnergyLevel {
energyNeeded4Stage3 := f.maxEnergyLevel - currentEnergy
totalTime += energyNeeded4Stage3 / (f.maxEnergyLevel - eightyPercentOfMaxEnergy) * 7200.0
}

if floatEquals(targetState.ChargingEnergy, f.maxEnergyLevel) {
return ChargingCost{
Duration: totalTime,
}
}

glog.Fatalf("Invalid charging state %#v\n", targetState)
return noNeedCharge
}

var epsilon float64 = 0.00000001

func floatEquals(a, b float64) bool {
if (a-b) < epsilon && (b-a) < epsilon {
return true
}
return false
}
71 changes: 71 additions & 0 deletions integration/oasis/chargingstrategy/chargingstrategy_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
package chargingstrategy

import (
"reflect"
"testing"
)

func TestFakeChargingStrategyCreator(t *testing.T) {
cases := []struct {
arrivalEnergyLevel float64
maxEnergyLevel float64
expectResult []ChargingCost
}{
{
10000,
50000,
[]ChargingCost{
ChargingCost{
Duration: 2400.0,
},
ChargingCost{
Duration: 6000.0,
},
ChargingCost{
Duration: 13200.0,
},
},
},
{
32000,
50000,
[]ChargingCost{
ChargingCost{
Duration: 0.0,
},
ChargingCost{
Duration: 2880.0,
},
ChargingCost{
Duration: 10080.0,
},
},
},
{
41000,
50000,
[]ChargingCost{
ChargingCost{
Duration: 0.0,
},
ChargingCost{
Duration: 0.0,
},
ChargingCost{
Duration: 6480.0,
},
},
},
}

for _, c := range cases {
var actualResult []ChargingCost
strategy := NewFakeChargingStrategyCreator(c.arrivalEnergyLevel, c.maxEnergyLevel)
for _, state := range strategy.CreateChargingStrategies() {
actualResult = append(actualResult, strategy.EvaluateCost(c.arrivalEnergyLevel, state))
}
if !reflect.DeepEqual(actualResult, c.expectResult) {
t.Errorf("parse case %#v, expect\n %#v but got\n %#v", c, c.expectResult, actualResult)
}
}
}
17 changes: 17 additions & 0 deletions integration/oasis/chargingstrategy/null_charge_strategy.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package chargingstrategy

type nullChargeStrategy struct {
}

// NewNullChargeStrategy creates nullChargeStrategy used to bypass unit tests
func NewNullChargeStrategy() *nullChargeStrategy {
return &nullChargeStrategy{}
}

func (f *nullChargeStrategy) CreateChargingStrategies() []ChargingStrategy {
return []ChargingStrategy{}
}

func (f *nullChargeStrategy) EvaluateCost(arrivalEnergy float64, targetState ChargingStrategy) ChargingCost {
return ChargingCost{}
}
1 change: 1 addition & 0 deletions integration/oasis/reachable_by_single_charge.go
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@ func generateResponse4SingleChargeStation(w http.ResponseWriter, req *oasis.Requ

station := new(oasis.ChargeStation)
station.WaitTime = 0.0
// @todo ChargeTime and ChargeRange need to be adjusted according to chargingstrategy
station.ChargeTime = 7200.0
station.ChargeRange = req.MaxRange
station.DetailURL = "url"
Expand Down
7 changes: 7 additions & 0 deletions integration/oasis/solutionformat/doc.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
/*
package solutionformat defines internal foramt for recording single solution result.
It isolates internal calculation logic and final restful response:
- Internal algorithm will calculate result in this format.
- In the end, oasis will use this format to generate restful response.
*/
package solutionformat
26 changes: 26 additions & 0 deletions integration/oasis/solutionformat/solution.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package solutionformat

// Solution contains summary and selected charge stations
type Solution struct {
Distance float64
Duration float64
RemainingRage float64
Weight float64
ChargeStations []*ChargeStation
}

// ChargeStation contains all information related with specific charge station
type ChargeStation struct {
Location Location
StationID string
ArrivalEnergy float64
WaitTime float64
ChargeTime float64
ChargeRange float64
}

// Location defines the geo location of a station
type Location struct {
Lat float64
Lon float64
}
24 changes: 19 additions & 5 deletions integration/oasis/stationfinder/stations_iterator_alg.go
Original file line number Diff line number Diff line change
Expand Up @@ -94,11 +94,19 @@ func CalcWeightBetweenChargeStationsPair(from nearbyStationsIterator, to nearbyS

// iterate table response result
var result []NeighborInfo
for i := range startPoints {
for j := range targetPoints {
for i, startPoint := range startPoints {
for j, targetPoint := range targetPoints {
result = append(result, NeighborInfo{
FromID: startIDs[i],
ToID: targetIDs[j],
FromLocation: StationCoordinate{
Lat: startPoint.Lat,
Lon: startPoint.Lon,
},
ToID: targetIDs[j],
ToLocation: StationCoordinate{
Lat: targetPoint.Lat,
Lon: targetPoint.Lon,
},
Cost: Cost{
Duration: *resp.Resp.Durations[i][j],
Distance: *resp.Resp.Distances[i][j],
Expand All @@ -118,8 +126,10 @@ type Cost struct {

// NeighborInfo represent cost information between two charge stations
type NeighborInfo struct {
FromID string
ToID string
FromID string
FromLocation StationCoordinate
ToID string
ToLocation StationCoordinate
Cost
}

Expand Down Expand Up @@ -149,6 +159,10 @@ type WeightBetweenNeighbors struct {
// - CalcWeightBetweenChargeStationsPair needs two iterators, one for nearbystationiterator
// represents from location and one for next location. An array of channel is created
// to represent whether specific iterator is ready or not.
// - The result of this function is channel of WeightBetweenNeighbors, the sequence of
// WeightBetweenNeighbors is important for future logic: first result is start -> first
// group of low energy charge stations, first group -> second group, ..., xxx group to
// end
// - All iterators has been recorded in iterators array
// @Todo: isIteratorReady could be removed later. When iterator is not ready, should
// pause inside iterator itself. That need refactor the design of stationfinder.
Expand Down
Loading