///|
pub(all) struct SimulationEngine {
station : @topology.Station
table : @interlocking.InterlockingTable
detector : @conflict.ConflictDetector
event_queue : PreallocatedEventQueue
trains : Map[String, SimulatedTrain]
mut current_cycle : Int64
mut current_time_ms : Int64
scan_period_ms : Int64
} derive(Debug)
///|
pub fn SimulationEngine::new(
station : @topology.Station,
table : @interlocking.InterlockingTable,
) -> SimulationEngine {
let detector = @conflict.ConflictDetector::new(table)
let queue = PreallocatedEventQueue::new(1024)
{
station,
table,
detector,
event_queue: queue,
trains: Map([]),
current_cycle: 0,
current_time_ms: 0,
scan_period_ms: 10,
}
}
///|
pub fn SimulationEngine::push_event(
self : SimulationEngine,
event : SimulationEvent,
) -> Bool {
self.event_queue.push(event)
}
///|
pub fn SimulationEngine::register_train(
self : SimulationEngine,
train : SimulatedTrain,
) -> Unit {
self.trains.set(train.train_id, train)
}
///|
pub fn SimulationEngine::audit(
self : SimulationEngine,
) -> @interlocking.SafetyAuditReport {
self.table.audit_safety(self.station)
}
///|
pub fn SimulationEngine::step_cycle_fast(
self : SimulationEngine,
) -> CycleReport {
self.current_cycle = self.current_cycle + 1
self.current_time_ms = self.current_time_ms + self.scan_period_ms
let mut events_processed = 0
// 1. Process queued events
while !self.event_queue.is_empty() {
match self.event_queue.pop() {
None => break
Some(ev) => {
events_processed = events_processed + 1
match ev.event_type {
EventRequestRoute =>
match ev.route_id {
Some(r_id) =>
if self.detector.is_route_allowed_fast(self.table, r_id) {
let _ = self.table.request_and_lock_route_fast(
self.station,
r_id,
)
}
None => ()
}
EventCancelRoute =>
match ev.route_id {
Some(r_id) => {
let _ = self.table.cancel_route(self.station, r_id)
}
None => ()
}
EventThrowSwitch =>
match (ev.turnout_id, ev.target_position) {
(Some(t_id), Some(pos)) => {
let _ = self.station.set_turnout_position(t_id, pos)
}
_ => ()
}
EventOccupyTrack =>
match ev.track_id {
Some(tr_id) => {
let _ = self.station.set_track_state(
tr_id,
Occupied,
ev.train_id,
)
}
None => ()
}
EventClearTrack =>
match ev.track_id {
Some(tr_id) => {
let _ = self.station.set_track_state(tr_id, Clear, "")
}
None => ()
}
EventInjectFault =>
match ev.track_id {
Some(tr_id) => {
let _ = self.station.set_track_state(tr_id, Fault, "")
}
None => ()
}
EventClearFault =>
match ev.track_id {
Some(tr_id) => {
let _ = self.station.set_track_state(tr_id, Clear, "")
}
None => ()
}
EventTrainStep => ()
}
}
}
}
// 2. Advance simulated trains
let delta_sec = Double::from_int(self.scan_period_ms.to_int()) / 1000.0
for _, train in self.trains {
if !train.is_finished {
match self.table.get_rule(train.route_id) {
Some(rule) =>
match train.step_movement(delta_sec, rule, self.station) {
Some(generated_ev) => {
let _ = self.event_queue.push(generated_ev)
}
None => ()
}
None => ()
}
}
}
// 3. Process all route slots using fixed numeric traversal.
let release_counters = self.process_active_routes_fast()
// 4. Enforce fail-safe signal drops before reading the fixed scan summary.
let emergency_actions = self.table.enforce_emergency_fail_safe(self.station)
let safety_summary = self.table.scan_safety_summary(self.station)
{
cycle_count: self.current_cycle,
events_processed,
routes_active: self.table.active_slot_count(),
safety_violations: safety_summary.fatal_count + safety_summary.warning_count,
active_slots_scanned: release_counters.active_slots_scanned,
sections_released: release_counters.sections_released,
switches_released: release_counters.switches_released,
emergency_actions,
execution_micro_seconds: 0,
}
}
///|
pub fn SimulationEngine::step_cycle(self : SimulationEngine) -> CycleReport {
self.step_cycle_fast()
}