///|
pub(all) struct InterlockingTable {
rules : Map[Int, InterlockingRule]
active_routes : Map[Int, ActiveRouteState]
active_route_flags : FixedArray[Bool]
signal_pair_index : Map[Int, RouteId]
track_to_routes : Map[Int, Array[RouteId]]
turnout_to_routes : Map[Int, Array[RouteId]]
acquired_turnout_scratch : FixedArray[@topology.TurnoutId]
acquired_track_scratch : FixedArray[@topology.TrackId]
mut acquired_turnout_count : Int
mut acquired_track_count : Int
mut next_route_id : Int
} derive(Debug)
///|
pub fn InterlockingTable::new() -> InterlockingTable {
{
rules: Map([]),
active_routes: Map([]),
active_route_flags: FixedArray::make(256, false),
signal_pair_index: Map([]),
track_to_routes: Map([]),
turnout_to_routes: Map([]),
acquired_turnout_scratch: FixedArray::make(256, @topology.TurnoutId::new(0)),
acquired_track_scratch: FixedArray::make(256, @topology.TrackId::new(0)),
acquired_turnout_count: 0,
acquired_track_count: 0,
next_route_id: 1,
}
}
///|
pub fn InterlockingTable::generate_pair_key(
entry : @topology.SignalId,
exit : @topology.SignalId,
) -> Int {
entry.id * 10000 + exit.id
}
///|
pub fn InterlockingTable::register_rule(
self : InterlockingTable,
name : String,
route_type : RouteType,
entry_signal : @topology.SignalId,
exit_signal : @topology.SignalId,
switches : Array[SwitchRequirement],
flank_switches : Array[FlankProtection],
track_sections : Array[@topology.TrackId],
hostile_route_ids : Array[RouteId],
target_aspect : @topology.SignalAspect,
approach_section : @topology.TrackId?,
) -> RouteId {
match
self.register_rule_checked(
name, route_type, entry_signal, exit_signal, switches, flank_switches, track_sections,
hostile_route_ids, target_aspect, approach_section,
) {
Ok(route_id) => route_id
Err(_) => RouteId::new(0)
}
}
///|
pub fn InterlockingTable::register_rule_checked(
self : InterlockingTable,
name : String,
route_type : RouteType,
entry_signal : @topology.SignalId,
exit_signal : @topology.SignalId,
switches : Array[SwitchRequirement],
flank_switches : Array[FlankProtection],
track_sections : Array[@topology.TrackId],
hostile_route_ids : Array[RouteId],
target_aspect : @topology.SignalAspect,
approach_section : @topology.TrackId?,
) -> Result[RouteId, String] {
if self.next_route_id <= 0 || self.next_route_id >= 256 {
return Err("Route capacity exhausted: maximum route id is 255")
}
let r_id = RouteId::new(self.next_route_id)
self.next_route_id = self.next_route_id + 1
let rule = InterlockingRule::new(
r_id, name, route_type, entry_signal, exit_signal, switches, flank_switches,
track_sections, hostile_route_ids, target_aspect, approach_section,
)
self.rules.set(r_id.id, rule)
// Route state is allocated during timetable/configuration setup. The hot
// scan path only replaces this existing map entry and never grows it.
self.active_routes.set(r_id.id, ActiveRouteState::new(rule))
let pair_key = InterlockingTable::generate_pair_key(entry_signal, exit_signal)
self.signal_pair_index.set(pair_key, r_id)
for tr in track_sections {
match self.track_to_routes.get(tr.id) {
Some(arr) => arr.push(r_id)
None => self.track_to_routes.set(tr.id, [r_id])
}
}
for sw in switches {
match self.turnout_to_routes.get(sw.turnout_id.id) {
Some(arr) => arr.push(r_id)
None => self.turnout_to_routes.set(sw.turnout_id.id, [r_id])
}
}
Ok(r_id)
}
///|
pub fn InterlockingTable::is_active_slot(
self : InterlockingTable,
slot : Int,
) -> Bool {
if slot < 0 || slot >= 256 {
false
} else {
self.active_route_flags[slot]
}
}
///|
pub fn InterlockingTable::active_slot_count(self : InterlockingTable) -> Int {
let mut count = 0
for i = 0; i < 256; i = i + 1 {
if self.active_route_flags[i] {
count = count + 1
}
}
count
}
///|
fn InterlockingTable::set_active_slot(
self : InterlockingTable,
route_id : RouteId,
active : Bool,
) -> Unit {
if route_id.id > 0 && route_id.id < 256 {
self.active_route_flags[route_id.id] = active
}
}
///|
fn InterlockingTable::deactivate_route(
self : InterlockingTable,
route_id : RouteId,
) -> Unit {
match self.active_routes.get(route_id.id) {
Some(active_route) => active_route.state = RouteInactive
None => ()
}
self.set_active_slot(route_id, false)
}
///|
fn InterlockingTable::activate_route(
self : InterlockingTable,
route_id : RouteId,
rule : InterlockingRule,
) -> Unit {
match self.active_routes.get(route_id.id) {
Some(active_route) => active_route.reset_for_rule(rule)
None => self.active_routes.set(route_id.id, ActiveRouteState::new(rule))
}
match self.active_routes.get(route_id.id) {
Some(active_route) => active_route.state = RouteLocked
None => ()
}
self.set_active_slot(route_id, true)
}
///|
pub fn InterlockingTable::get_rule(
self : InterlockingTable,
route_id : RouteId,
) -> InterlockingRule? {
self.rules.get(route_id.id)
}
///|
pub fn InterlockingTable::lookup_route_by_signals(
self : InterlockingTable,
entry : @topology.SignalId,
exit : @topology.SignalId,
) -> RouteId? {
let key = InterlockingTable::generate_pair_key(entry, exit)
self.signal_pair_index.get(key)
}
///|
pub fn InterlockingTable::get_active_route(
self : InterlockingTable,
route_id : RouteId,
) -> ActiveRouteState? {
match self.active_routes.get(route_id.id) {
Some(active_route) =>
if self.is_route_reserved(route_id) {
Some(active_route)
} else {
None
}
None => None
}
}
///|
pub fn InterlockingTable::is_route_active(
self : InterlockingTable,
route_id : RouteId,
) -> Bool {
match self.active_routes.get(route_id.id) {
None => false
Some(ar) =>
match ar.state {
RouteInactive | RouteFault => false
_ => true
}
}
}
///|
pub fn InterlockingTable::is_route_reserved(
self : InterlockingTable,
route_id : RouteId,
) -> Bool {
match self.active_routes.get(route_id.id) {
None => false
Some(ar) =>
match ar.state {
RouteInactive => false
_ => true
}
}
}
///|
pub fn InterlockingTable::get_routes_by_track(
self : InterlockingTable,
track_id : @topology.TrackId,
) -> Array[RouteId] {
match self.track_to_routes.get(track_id.id) {
None => []
Some(arr) => arr
}
}