From cf0f034328021ee18ed380a3976f0cb851073295 Mon Sep 17 00:00:00 2001 From: codebear801 Date: Mon, 20 Apr 2020 18:00:38 -0700 Subject: [PATCH] refactor: refactor charge station's logic. issue: https://github.com/Telenav/osrm-backend/issues/292 --- .../oasis/reachable_by_multiple_charge.go | 6 +- .../service/oasis/stationgraph/graph.go | 71 +++--- .../oasis/stationgraph/graph_interface.go | 12 +- .../service/oasis/stationgraph/graph_mock.go | 64 ++++- .../oasis/stationgraph/station_graph.go | 224 ++++++------------ 5 files changed, 185 insertions(+), 192 deletions(-) diff --git a/integration/service/oasis/reachable_by_multiple_charge.go b/integration/service/oasis/reachable_by_multiple_charge.go index 736254b9a3e..cfd5bb8defd 100644 --- a/integration/service/oasis/reachable_by_multiple_charge.go +++ b/integration/service/oasis/reachable_by_multiple_charge.go @@ -38,8 +38,10 @@ func generateSolutionsWithEarlistArrival(oasisReq *oasis.Request, routeResp *rou chargeLocations := chargeLocationSelection(oasisReq, routeResp) for _, locations := range chargeLocations { c := stationfinderalg.CalculateWeightBetweenNeighbors(locations, oc, finder) - internalSolutions := stationgraph.NewStationGraph(c, oasisReq.CurrRange, oasisReq.MaxRange, - chargingstrategy.NewFakeChargingStrategy(oasisReq.MaxRange)).GenerateChargeSolutions() + querier := stationfinderalg.NewQuerierBasedOnWeightBetweenNeighborsChan(c) + internalSolutions := stationgraph.NewStationGraph(oasisReq.CurrRange, oasisReq.MaxRange, + chargingstrategy.NewFakeChargingStrategy(oasisReq.MaxRange), + querier).GenerateChargeSolutions() for _, sol := range internalSolutions { targetSolution := sol.Convert2ExternalSolution() diff --git a/integration/service/oasis/stationgraph/graph.go b/integration/service/oasis/stationgraph/graph.go index 464da7edb04..9d28a80dbe4 100644 --- a/integration/service/oasis/stationgraph/graph.go +++ b/integration/service/oasis/stationgraph/graph.go @@ -16,7 +16,20 @@ type graph struct { startNodeID nodeID endNodeID nodeID strategy chargingstrategy.Strategy - query connectivitymap.Querier + querier connectivitymap.Querier +} + +// NewGraph creates new graph which implements IGraph +func NewGraph(strategy chargingstrategy.Strategy, query connectivitymap.Querier) IGraph { + return &graph{ + nodeContainer: newNodeContainer(), + adjacentList: make(map[nodeID][]nodeID), + edgeData: make(map[edgeID]*edge), + startNodeID: invalidNodeID, + endNodeID: invalidNodeID, + strategy: strategy, + querier: query, + } } func (g *graph) Node(id nodeID) *node { @@ -48,6 +61,20 @@ func (g *graph) Edge(from, to nodeID) *edge { return g.edgeData[edgeID] } +// SetStart generates start node for the graph +func (g *graph) SetStart(stationID string, targetState chargingstrategy.State, location locationInfo) IGraph { + n := g.nodeContainer.addNode(stationID, targetState, location) + g.startNodeID = n.id + return g +} + +// SetEnd generates end node for the graph +func (g *graph) SetEnd(stationID string, targetState chargingstrategy.State, location locationInfo) IGraph { + n := g.nodeContainer.addNode(stationID, targetState, location) + g.endNodeID = n.id + return g +} + func (g *graph) StartNodeID() nodeID { return g.startNodeID } @@ -60,16 +87,20 @@ func (g *graph) ChargeStrategy() chargingstrategy.Strategy { return g.strategy } +func (g *graph) StationID(id nodeID) string { + return g.nodeContainer.stationID(id) +} + func (g *graph) getPhysicalAdjacentNodes(id nodeID) []*connectivitymap.QueryResult { stationID := g.nodeContainer.stationID(id) if stationID == invalidStationID { glog.Errorf("Query getPhysicalAdjacentNodes with invalid node %#v and result %#v\n", id, invalidStationID) return nil } - return g.query.NearByStationQuery(stationID) + return g.querier.NearByStationQuery(stationID) } -func (g *graph) createLogicalNodes(from nodeID, toStationID string, toLocation nav.Location, distance, duration float64) []*node { +func (g *graph) createLogicalNodes(from nodeID, toStationID string, toLocation *nav.Location, distance, duration float64) []*node { results := make([]*node, 0, 10) for _, state := range g.strategy.CreateChargingStates() { @@ -108,37 +139,3 @@ func (g *graph) buildAdjacentList(id nodeID) []nodeID { return adjacentNodeIDs } - -func (g *graph) accumulateDistanceAndDuration(from nodeID, to nodeID, distance, duration *float64) { - if g.Node(from) == nil { - glog.Fatalf("While calling accumulateDistanceAndDuration, incorrect nodeID passed into graph %v\n", from) - } - - if g.Node(to) == nil { - glog.Fatalf("While calling accumulateDistanceAndDuration, incorrect nodeID passed into graph %v\n", to) - } - - if g.Edge(from, to) == nil { - glog.Errorf("Passing un-connect fromNodeID %#v and toNodeID %#v into accumulateDistanceAndDuration.\n", from, to) - } - - *distance += g.Edge(from, to).distance - *duration += g.Edge(from, to).duration + g.Node(to).chargeTime - -} - -func (g *graph) getChargeInfo(n nodeID) chargeInfo { - if g.Node(n) == nil { - glog.Fatalf("While calling getChargeInfo, incorrect nodeID passed into graph %v\n", n) - } - - return g.Node(n).chargeInfo -} - -func (g *graph) getLocationInfo(n nodeID) locationInfo { - if g.Node(n) == nil { - glog.Fatalf("While calling getLocationInfo, incorrect nodeID passed into graph %v\n", n) - } - - return g.Node(n).locationInfo -} diff --git a/integration/service/oasis/stationgraph/graph_interface.go b/integration/service/oasis/stationgraph/graph_interface.go index e9033157976..34407138483 100644 --- a/integration/service/oasis/stationgraph/graph_interface.go +++ b/integration/service/oasis/stationgraph/graph_interface.go @@ -14,6 +14,12 @@ type IGraph interface { // Edge returns edge information between given two nodes Edge(from, to nodeID) *edge + // SetStart generates start node for the graph + SetStart(stationID string, targetState chargingstrategy.State, location locationInfo) IGraph + + // SetEnd generates end node for the graph + SetEnd(stationID string, targetState chargingstrategy.State, location locationInfo) IGraph + // StartNodeID returns start node's ID for given graph StartNodeID() nodeID @@ -22,11 +28,7 @@ type IGraph interface { // ChargeStrategy returns charge strategy used for graph construction ChargeStrategy() chargingstrategy.Strategy -} -// IStationInfo defines station related information -type IStationInfo interface { + // StationID returns original stationID from internal nodeID StationID(id nodeID) string - - StationLocation(id nodeID) *locationInfo } diff --git a/integration/service/oasis/stationgraph/graph_mock.go b/integration/service/oasis/stationgraph/graph_mock.go index b1067aed923..6226ff81ea7 100644 --- a/integration/service/oasis/stationgraph/graph_mock.go +++ b/integration/service/oasis/stationgraph/graph_mock.go @@ -78,6 +78,13 @@ func NewMockGraph1() IGraph { }, }, }, + []string{ + "node_0", + "node_1", + "node_2", + "node_3", + "node_4", + }, map[nodeID][]*edgeIDAndData{ // node_0 -> node_1, duration = 30, distance = 30 // node_0 -> node_2, duration = 20, distance = 20 @@ -295,6 +302,17 @@ func NewMockGraph2() IGraph { }, }, }, + []string{ + "node_0", + "node_1", + "node_2", + "node_3", + "node_4", + "node_5", + "node_6", + "node_7", + "node_8", + }, map[nodeID][]*edgeIDAndData{ // node_0 -> node_1, duration = 30, distance = 30 // node_0 -> node_2, duration = 20, distance = 20 @@ -619,6 +637,17 @@ func NewMockGraph3() IGraph { }, }, }, + []string{ + "node_0", + "node_1", + "node_2", + "node_3", + "node_4", + "node_5", + "node_6", + "node_7", + "node_8", + }, map[nodeID][]*edgeIDAndData{ // node_0 -> node_1, duration = 15, distance = 15 // node_0 -> node_2, duration = 20, distance = 20 @@ -944,6 +973,17 @@ func NewMockGraph4() IGraph { }, }, }, + []string{ + "node_0", + "node_1", + "node_2", + "node_3", + "node_4", + "node_5", + "node_6", + "node_7", + "node_8", + }, map[nodeID][]*edgeIDAndData{ // node_0 -> node_1, duration = 15, distance = 15 // node_0 -> node_2, duration = 20, distance = 20 @@ -1132,9 +1172,10 @@ func NewMockGraph4() IGraph { } type mockGraph struct { - nodes []*node - edges map[nodeID][]*edgeIDAndData - strategy chargingstrategy.Strategy + nodes []*node + stationIDs []string + edges map[nodeID][]*edgeIDAndData + strategy chargingstrategy.Strategy } func (graph *mockGraph) Node(id nodeID) *node { @@ -1173,6 +1214,16 @@ func (graph *mockGraph) Edge(from, to nodeID) *edge { return nil } +// SetStart generates start node for the graph +func (graph *mockGraph) SetStart(stationID string, targetState chargingstrategy.State, location locationInfo) IGraph { + return graph +} + +// SetEnd generates end node for the graph +func (graph *mockGraph) SetEnd(stationID string, targetState chargingstrategy.State, location locationInfo) IGraph { + return graph +} + func (graph *mockGraph) StartNodeID() nodeID { return invalidNodeID } @@ -1191,3 +1242,10 @@ func (graph *mockGraph) isValidNodeID(id nodeID) bool { } return true } + +func (graph *mockGraph) StationID(id nodeID) string { + if id < 0 || int(id) >= len(graph.stationIDs) { + return invalidStationID + } + return graph.stationIDs[id] +} diff --git a/integration/service/oasis/stationgraph/station_graph.go b/integration/service/oasis/stationgraph/station_graph.go index 9abd21acaba..44a0118cea2 100644 --- a/integration/service/oasis/stationgraph/station_graph.go +++ b/integration/service/oasis/stationgraph/station_graph.go @@ -3,54 +3,66 @@ package stationgraph import ( "github.com/Telenav/osrm-backend/integration/pkg/api/nav" "github.com/Telenav/osrm-backend/integration/service/oasis/chargingstrategy" + "github.com/Telenav/osrm-backend/integration/service/oasis/connectivitymap" "github.com/Telenav/osrm-backend/integration/service/oasis/solution" "github.com/Telenav/osrm-backend/integration/service/oasis/stationfinder/stationfindertype" "github.com/golang/glog" ) type stationGraph struct { - g *graph - stationID2Nodes map[string][]*node - num2StationID map[uint32]string // from number to original stationID - // stationID is converted to numbers(0, 1, 2 ...) based on visit sequence - - stationsCount uint32 - strategy chargingstrategy.Strategy + g IGraph + querier connectivitymap.Querier + strategy chargingstrategy.Strategy } // NewStationGraph creates station graph from channel -func NewStationGraph(c chan stationfindertype.WeightBetweenNeighbors, currEnergyLevel, maxEnergyLevel float64, strategy chargingstrategy.Strategy) *stationGraph { +func NewStationGraph(currEnergyLevel, maxEnergyLevel float64, strategy chargingstrategy.Strategy, querier connectivitymap.Querier) *stationGraph { sg := &stationGraph{ - g: &graph{ - startNodeID: invalidNodeID, - endNodeID: invalidNodeID, - strategy: strategy, - }, - stationID2Nodes: make(map[string][]*node), - num2StationID: make(map[uint32]string), - stationsCount: 0, - strategy: strategy, + g: NewGraph(strategy, querier), + querier: querier, + strategy: strategy, } - for item := range c { - if item.Err != nil { - glog.Errorf("Met error during constructing stationgraph, error = %v", item.Err) - return nil - } + if !sg.setStartAndEnd(currEnergyLevel, maxEnergyLevel) { + return nil + } - for _, neighborInfo := range item.NeighborsInfo { - sg.buildNeighborInfoBetweenNodes(neighborInfo, currEnergyLevel, maxEnergyLevel) - } + return sg +} + +func (sg *stationGraph) setStartAndEnd(currEnergyLevel, maxEnergyLevel float64) bool { + startLocation := sg.querier.GetLocation(stationfindertype.OrigLocationID) + if startLocation == nil { + glog.Errorf("Failed to find %#v from Querier's GetLocation()\n", stationfindertype.OrigLocationID) + return false + } + + endLocation := sg.querier.GetLocation(stationfindertype.DestLocationID) + if startLocation == nil { + glog.Errorf("Failed to find %#v from Querier's GetLocation()\n", stationfindertype.DestLocationID) + return false } - return sg.constructGraph() + sg.g = sg.g.SetStart(stationfindertype.OrigLocationID, + chargingstrategy.State{ + Energy: currEnergyLevel, + }, + locationInfo{ + startLocation.Lat, + startLocation.Lon}) + + sg.g = sg.g.SetEnd(stationfindertype.DestLocationID, + chargingstrategy.State{}, + locationInfo{ + endLocation.Lat, + endLocation.Lon}) + + return true } // GenerateChargeSolutions creates creates charge solutions for staion graph func (sg *stationGraph) GenerateChargeSolutions() []*solution.Solution { - //stationNodes := dijkstra(sg.g) - // @todo - var stationNodes []nodeID + stationNodes := dijkstra(sg.g, sg.g.StartNodeID(), sg.g.EndNodeID()) if nil == stationNodes { glog.Warning("Failed to generate charge stations for stationGraph.\n") return nil @@ -63,156 +75,78 @@ func (sg *stationGraph) GenerateChargeSolutions() []*solution.Solution { var totalDistance, totalDuration float64 // accumulate information: start node -> first charge station - startNodeID := sg.stationID2Nodes[stationfindertype.OrigLocationID][0].id - sg.g.accumulateDistanceAndDuration(startNodeID, stationNodes[0], &totalDistance, &totalDuration) + startNodeID := sg.g.StartNodeID() + accumulateDistanceAndDuration(sg.g, startNodeID, stationNodes[0], &totalDistance, &totalDuration) // accumulate information: first charge station -> second charge station -> ... -> end node for i := 0; i < len(stationNodes); i++ { if i != len(stationNodes)-1 { - sg.g.accumulateDistanceAndDuration(stationNodes[i], stationNodes[i+1], &totalDistance, &totalDuration) + accumulateDistanceAndDuration(sg.g, stationNodes[i], stationNodes[i+1], &totalDistance, &totalDuration) } else { - endNodeID := sg.stationID2Nodes[stationfindertype.DestLocationID][0].id - sg.g.accumulateDistanceAndDuration(stationNodes[i], endNodeID, &totalDistance, &totalDuration) + endNodeID := sg.g.EndNodeID() + accumulateDistanceAndDuration(sg.g, stationNodes[i], endNodeID, &totalDistance, &totalDuration) } // construct station information station := &solution.ChargeStation{} - station.ArrivalEnergy = sg.g.getChargeInfo(stationNodes[i]).arrivalEnergy - station.ChargeRange = sg.g.getChargeInfo(stationNodes[i]).targetState.Energy - station.ChargeTime = sg.g.getChargeInfo(stationNodes[i]).chargeTime + station.ArrivalEnergy = getChargeInfo(sg.g, stationNodes[i]).arrivalEnergy + station.ChargeRange = getChargeInfo(sg.g, stationNodes[i]).targetState.Energy + station.ChargeTime = getChargeInfo(sg.g, stationNodes[i]).chargeTime station.Location = nav.Location{ - Lat: sg.g.getLocationInfo(stationNodes[i]).lat, - Lon: sg.g.getLocationInfo(stationNodes[i]).lon, + Lat: getLocationInfo(sg.g, stationNodes[i]).lat, + Lon: getLocationInfo(sg.g, stationNodes[i]).lon, } - station.StationID = sg.num2StationID[uint32(stationNodes[i])] + station.StationID = sg.g.StationID(stationNodes[i]) sol.ChargeStations = append(sol.ChargeStations, station) } sol.Distance = totalDistance sol.Duration = totalDuration - sol.RemainingRage = sg.stationID2Nodes[stationfindertype.DestLocationID][0].arrivalEnergy + sol.RemainingRage = getChargeInfo(sg.g, sg.g.EndNodeID()).arrivalEnergy result = append(result, sol) return result } -func (sg *stationGraph) buildNeighborInfoBetweenNodes(neighborInfo stationfindertype.NeighborInfo, currEnergyLevel, maxEnergyLevel float64) { - // for _, fromNode := range sg.getChargeStationsNodes(neighborInfo.FromID, neighborInfo.FromLocation, currEnergyLevel, maxEnergyLevel) { - // for _, toNode := range sg.getChargeStationsNodes(neighborInfo.ToID, neighborInfo.ToLocation, currEnergyLevel, maxEnergyLevel) { - // fromNode.neighbors = append(fromNode.neighbors, &neighbor{ - // targetNodeID: toNode.id, - // distance: neighborInfo.Distance, - // duration: neighborInfo.Duration, - // }) - // } - // } -} +func accumulateDistanceAndDuration(g IGraph, from nodeID, to nodeID, distance, duration *float64) { + if g.Node(from) == nil { + glog.Fatalf("While calling accumulateDistanceAndDuration, incorrect nodeID passed into graph %v\n", from) + } -func (sg *stationGraph) getChargeStationsNodes(id string, location nav.Location, currEnergyLevel, maxEnergyLevel float64) []*node { - if _, ok := sg.stationID2Nodes[id]; !ok { - if sg.isStart(id) { - sg.constructStartNode(id, location, currEnergyLevel) - } else if sg.isEnd(id) { - sg.constructEndNode(id, location) - } else { - var nodes []*node - for _, state := range sg.strategy.CreateChargingStates() { - n := &node{ - id: nodeID(sg.stationsCount), - chargeInfo: chargeInfo{ - targetState: state, - }, - locationInfo: locationInfo{ - lat: location.Lat, - lon: location.Lon, - }, - } - nodes = append(nodes, n) - sg.num2StationID[sg.stationsCount] = id - sg.stationsCount += 1 - } - - sg.stationID2Nodes[id] = nodes - } + if g.Node(to) == nil { + glog.Fatalf("While calling accumulateDistanceAndDuration, incorrect nodeID passed into graph %v\n", to) } - return sg.stationID2Nodes[id] -} -func (sg *stationGraph) isStart(id string) bool { - return id == stationfindertype.OrigLocationID -} + if g.Edge(from, to) == nil { + glog.Errorf("Passing un-connect fromNodeID %#v and toNodeID %#v into accumulateDistanceAndDuration.\n", from, to) + } -func (sg *stationGraph) isEnd(id string) bool { - return id == stationfindertype.DestLocationID -} + *distance += g.Edge(from, to).distance + *duration += g.Edge(from, to).duration + g.Node(to).chargeTime -func (sg *stationGraph) getStationID(id nodeID) string { - return sg.num2StationID[uint32(id)] } -func (sg *stationGraph) constructStartNode(id string, location nav.Location, currEnergyLevel float64) { - - n := &node{ - id: nodeID(sg.stationsCount), - chargeInfo: chargeInfo{ - arrivalEnergy: currEnergyLevel, - chargeTime: 0.0, - targetState: chargingstrategy.State{ - Energy: currEnergyLevel, - }, - }, - locationInfo: locationInfo{ - lat: location.Lat, - lon: location.Lon, - }, +func getChargeInfo(g IGraph, n nodeID) chargeInfo { + if g.Node(n) == nil { + glog.Fatalf("While calling getChargeInfo, incorrect nodeID passed into graph %v\n", n) } - sg.stationID2Nodes[id] = []*node{n} - sg.num2StationID[sg.stationsCount] = id - sg.stationsCount += 1 -} -func (sg *stationGraph) constructEndNode(id string, location nav.Location) { + return g.Node(n).chargeInfo +} - n := &node{ - id: nodeID(sg.stationsCount), - locationInfo: locationInfo{ - lat: location.Lat, - lon: location.Lon, - }, +func getLocationInfo(g IGraph, n nodeID) locationInfo { + if g.Node(n) == nil { + glog.Fatalf("While calling getLocationInfo, incorrect nodeID passed into graph %v\n", n) } - sg.stationID2Nodes[id] = []*node{n} - sg.num2StationID[sg.stationsCount] = id - sg.stationsCount += 1 + + return g.Node(n).locationInfo } -func (sg *stationGraph) constructGraph() *stationGraph { - // sg.g.nodes = make([]*node, int(sg.stationsCount)) - - // for k, v := range sg.stationID2Nodes { - // if sg.isStart(k) { - // sg.g.startNodeID = v[0].id - // } - - // if sg.isEnd(k) { - // sg.g.endNodeID = v[0].id - // } - - // for _, n := range v { - // sg.g.nodes[n.id] = n - // } - // } - - // if sg.g.startNodeID == invalidNodeID { - // glog.Error("Invalid nodeid generated for start node.\n") - // return nil - // } else if sg.g.endNodeID == invalidNodeID { - // glog.Error("Invalid nodeid generated for start node.\n") - // return nil - // } else if len(sg.g.nodes) != int(sg.stationsCount) { - // glog.Errorf("Invalid nodes generated, len(sg.g.nodes) is %d while sg.stationsCount is %d.\n", len(sg.g.nodes), sg.stationsCount) - // return nil - // } +func (sg *stationGraph) isStart(id string) bool { + return id == stationfindertype.OrigLocationID +} - return sg +func (sg *stationGraph) isEnd(id string) bool { + return id == stationfindertype.DestLocationID }