///|
pub(all) enum CostCriteria {
MinimumTransportDelay
MinimumCycleTime
MinimumEnergyLoss
BalancedManufacturingCost
} derive(Debug, Eq)
///|
pub(all) struct PathResult {
nodes : Array[String]
edges : Array[@graph.Edge]
total_cost : Double
total_delay_sec : Double
bottleneck_bandwidth : Double
} derive(Debug, Eq)
///|
fn compute_edge_weight(
edge : @graph.Edge,
target_node : @graph.Node?,
criteria : CostCriteria,
) -> Double {
let delay = edge.transport_delay_sec()
let energy = edge.energy_loss_kw()
let cycle_time = match target_node {
Some(n) => n.cycle_time_sec()
None => 0.0
}
match criteria {
MinimumTransportDelay => delay
MinimumCycleTime => cycle_time + delay
MinimumEnergyLoss => energy * 10.0 + delay
BalancedManufacturingCost => cycle_time * 0.5 + delay + energy * 2.0
}
}
///|
pub fn dijkstra_shortest_path(
graph : @graph.FactoryGraph,
source : String,
target : String,
flow_type? : @types.FlowType = @types.FlowType::MaterialFlow,
criteria? : CostCriteria = MinimumTransportDelay,
) -> PathResult? {
if !graph.has_node(source) || !graph.has_node(target) {
return None
}
if source == target {
return Some({
nodes: [source],
edges: [],
total_cost: 0.0,
total_delay_sec: 0.0,
bottleneck_bandwidth: 1.0e9,
})
}
let dist : Map[String, Double] = Map([])
let prev_node : Map[String, String] = Map([])
let prev_edge : Map[String, @graph.Edge] = Map([])
let visited : Map[String, Bool] = Map([])
dist[source] = 0.0
while true {
let mut min_node : String? = None
let mut min_dist = 1.0e12
for node_id, d in dist {
if !visited.contains(node_id) && d < min_dist {
min_dist = d
min_node = Some(node_id)
}
}
match min_node {
None => break
Some(u) => {
if u == target {
break
}
visited[u] = true
let out_edges = graph.get_layer_out_edges(flow_type, u)
for edge in out_edges {
if edge.is_active() {
let v = edge.target_id()
let v_node = graph.get_node(v)
let w = compute_edge_weight(edge, v_node, criteria)
let new_dist = min_dist + w
let old_dist = match dist.get(v) {
Some(od) => od
None => 1.0e12
}
if new_dist < old_dist {
dist[v] = new_dist
prev_node[v] = u
prev_edge[v] = edge
}
}
}
}
}
}
if !dist.contains(target) {
return None
}
// Reconstruct path
let path_nodes = []
let path_edges = []
let mut curr = target
path_nodes.push(curr)
while curr != source {
match prev_node.get(curr) {
Some(p) => {
match prev_edge.get(curr) {
Some(e) => path_edges.push(e)
None => ()
}
curr = p
path_nodes.push(curr)
}
None => break
}
}
// Reverse path
let rev_nodes = []
for i = path_nodes.length() - 1; i >= 0; i = i - 1 {
rev_nodes.push(path_nodes[i])
}
let rev_edges = []
for i = path_edges.length() - 1; i >= 0; i = i - 1 {
rev_edges.push(path_edges[i])
}
let mut total_delay = 0.0
let mut min_bw = 1.0e9
for e in rev_edges {
total_delay = total_delay + e.transport_delay_sec()
if e.bandwidth() < min_bw {
min_bw = e.bandwidth()
}
}
let total_cost = match dist.get(target) {
Some(c) => c
None => 0.0
}
Some({
nodes: rev_nodes,
edges: rev_edges,
total_cost,
total_delay_sec: total_delay,
bottleneck_bandwidth: min_bw,
})
}