///|
pub(all) struct RedundancyReport {
overall_resilience_index : Double
fully_redundant_routes_count : Int
single_point_vulnerable_stations : Array[String]
bypass_path_available : Bool
resilience_grade : String
} derive(Debug, Eq)
///|
pub fn evaluate_line_redundancy(
graph : @graph.FactoryGraph,
source_id : String,
target_id : String,
) -> RedundancyReport {
let disjoint_paths = @algorithms.find_disjoint_paths(
graph,
source_id,
target_id,
flow_type=@types.FlowType::MaterialFlow,
node_disjoint=true,
)
let k_paths = @algorithms.k_shortest_paths(graph, source_id, target_id, 3)
let spofs = @algorithms.find_critical_spof_assets(graph)
let disjoint_count = disjoint_paths.length()
let has_bypass = disjoint_count > 1 || k_paths.length() > 1
let mut score = 50.0
if disjoint_count >= 2 {
score = score + 30.0
}
if k_paths.length() >= 2 {
score = score + 10.0
}
let spof_penalty = spofs.single_points_of_failure.length().to_double() * 5.0
score = score - spof_penalty
if score < 0.0 {
score = 0.0
}
if score > 100.0 {
score = 100.0
}
let grade = if score >= 85.0 {
"A (Highly Resilient - Redundant Topology)"
} else if score >= 65.0 {
"B (Adequate Resilience - Minor Single Points of Failure)"
} else if score >= 40.0 {
"C (Moderate Risk - Critical Serial Dependencies)"
} else {
"D (High Vulnerability - Immediate Line Redundancy Needed)"
}
{
overall_resilience_index: score,
fully_redundant_routes_count: disjoint_count,
single_point_vulnerable_stations: spofs.single_points_of_failure,
bypass_path_available: has_bypass,
resilience_grade: grade,
}
}