///|
pub(all) struct SafetyInvariantViolation {
violation_type : String
description : String
affected_route_id : RouteId?
} derive(Debug)
///|
pub fn InterlockingTable::verify_all_safety_invariants(
self : InterlockingTable,
station : @topology.Station,
) -> Array[SafetyInvariantViolation] {
let violations : Array[SafetyInvariantViolation] = []
let report = self.audit_safety(station)
for violation in report.violations() {
violations.push({
violation_type: violation.kind.to_string(),
description: violation.description,
affected_route_id: violation.affected_route_id,
})
}
violations
}
///|
fn InterlockingTable::route_has_emergency_condition(
self : InterlockingTable,
station : @topology.Station,
active_route : ActiveRouteState,
) -> (Bool, Bool) {
let rule = active_route.rule
let mut should_drop_signal = false
let mut should_fault_route = false
for sw in rule.switches {
match station.graph.get_turnout(sw.turnout_id) {
None => {
should_drop_signal = true
should_fault_route = true
}
Some(turnout) =>
if turnout.position != sw.required_position || !turnout.is_locked {
should_drop_signal = true
should_fault_route = true
}
}
}
for flank in rule.flank_switches {
match station.graph.get_turnout(flank.turnout_id) {
None => {
should_drop_signal = true
should_fault_route = true
}
Some(turnout) =>
if turnout.position != flank.required_position || !turnout.is_locked {
should_drop_signal = true
should_fault_route = true
}
}
}
for hostile_id in rule.hostile_route_ids {
if self.is_route_reserved(hostile_id) {
should_drop_signal = true
should_fault_route = true
}
}
match station.graph.get_signal(rule.entry_signal) {
None => {
should_drop_signal = true
should_fault_route = true
}
Some(signal) => {
if active_route.state == RouteFault && signal.current_aspect != Red {
should_drop_signal = true
should_fault_route = true
}
for track_id in rule.track_sections {
match station.graph.get_track(track_id) {
None => {
should_drop_signal = true
should_fault_route = true
}
Some(track) => {
if track.state == Fault {
should_drop_signal = true
should_fault_route = true
}
if signal.current_aspect.is_proceed() &&
(track.state == Occupied || track.state == Fault) {
should_drop_signal = true
}
}
}
}
}
}
if !active_route.lifecycle_snapshot_is_safe() {
should_drop_signal = true
should_fault_route = true
}
(should_drop_signal, should_fault_route)
}
///|
pub fn InterlockingTable::enforce_emergency_fail_safe(
self : InterlockingTable,
station : @topology.Station,
) -> Int {
let mut count = 0
for _, active_route in self.active_routes {
if !self.is_route_reserved(active_route.rule.id) {
continue
}
let (should_drop_signal, should_fault_route) = self.route_has_emergency_condition(
station, active_route,
)
if should_drop_signal {
let signal_needs_drop = match
station.graph.get_signal(active_route.rule.entry_signal) {
Some(signal) => signal.current_aspect.is_proceed()
None => true
}
if signal_needs_drop {
let _ = station.set_signal_aspect(
active_route.rule.entry_signal,
@topology.SignalAspect::Red,
)
count = count + 1
}
if should_fault_route {
active_route.state = RouteFault
}
}
}
count
}