///|
/// A semantic category for a location in an evacuation map.
pub(all) enum PlaceKind {
Room
Corridor
Stair
Assembly
Exit
} derive(Eq, Debug)
///|
/// A directed connection category.
pub(all) enum PassageKind {
Door
Hallway
Stairway
Ramp
Outdoor
} derive(Eq, Debug)
///|
/// Severity used by validation diagnostics.
pub(all) enum Severity {
Info
Warning
Error
} derive(Eq, Debug)
///|
/// Mobility profile of an evacuee group.
pub(all) enum Mobility {
Fast
Standard
Slow
Assisted
} derive(Eq, Debug)
///|
/// A location is a graph vertex with geometry and holding capacity.
pub struct Place {
id : String
label : String
kind : PlaceKind
floor : Int
x : Double
y : Double
capacity : Int
initial_risk : Double
} derive(Eq, Debug)
///|
/// A directed graph edge used by routing and simulation.
pub struct Passage {
id : String
from_id : String
to_id : String
kind : PassageKind
length_m : Double
width_m : Double
flow_per_second : Double
bidirectional : Bool
accessible : Bool
enabled : Bool
} derive(Eq, Debug)
///|
/// Population initially located at one place.
pub struct PopulationGroup {
id : String
place_id : String
count : Int
mobility : Mobility
response_delay : Int
familiarity : Double
} derive(Eq, Debug)
///|
/// A time-based change applied to the simulation.
pub(all) enum ScenarioEvent {
ClosePassage(String, Int)
OpenPassage(String, Int)
Ignite(String, Int, Double)
ClearRisk(String, Int)
} derive(Eq, Debug)
///|
/// Complete immutable input to validation and simulation.
pub struct EvacuationPlan {
name : String
mut timestep_seconds : Double
places : Array[Place]
passages : Array[Passage]
populations : Array[PopulationGroup]
events : Array[ScenarioEvent]
} derive(Eq, Debug)
///|
/// A diagnostic produced while validating a plan.
pub struct Diagnostic {
code : String
severity : Severity
subject : String
message : String
hint : String
} derive(Eq, Debug)
///|
/// A compact point used by SVG rendering.
pub struct Point {
x : Double
y : Double
} derive(Eq, Debug)
///|
/// Construct a place with explicit properties.
pub fn Place::new(
id : String,
label : String,
kind : PlaceKind,
floor : Int,
x : Double,
y : Double,
capacity : Int,
) -> Place {
{ id, label, kind, floor, x, y, capacity, initial_risk: 0.0 }
}
///|
/// Construct a directed passage with practical defaults.
pub fn Passage::new(
id : String,
from_id : String,
to_id : String,
kind : PassageKind,
length_m : Double,
width_m : Double,
) -> Passage {
{
id,
from_id,
to_id,
kind,
length_m,
width_m,
flow_per_second: width_m * 1.25,
bidirectional: true,
accessible: kind != Stairway,
enabled: true,
}
}
///|
/// Construct a population group.
pub fn PopulationGroup::new(
id : String,
place_id : String,
count : Int,
mobility : Mobility,
) -> PopulationGroup {
{ id, place_id, count, mobility, response_delay: 0, familiarity: 1.0 }
}
///|
/// Construct an empty plan.
pub fn EvacuationPlan::new(name : String) -> EvacuationPlan {
{
name,
timestep_seconds: 1.0,
places: [],
passages: [],
populations: [],
events: [],
}
}
///|
/// Return the speed multiplier of a mobility profile.
pub fn Mobility::speed_factor(self : Mobility) -> Double {
match self {
Fast => 1.25
Standard => 1.0
Slow => 0.65
Assisted => 0.45
}
}
///|
/// Whether a mobility profile requires accessible passages.
pub fn Mobility::requires_accessible(self : Mobility) -> Bool {
match self {
Assisted => true
_ => false
}
}
///|
/// Stable textual name used in machine-readable reports.
pub fn PlaceKind::code(self : PlaceKind) -> String {
match self {
Room => "room"
Corridor => "corridor"
Stair => "stair"
Assembly => "assembly"
Exit => "exit"
}
}
///|
/// Stable textual name used in machine-readable reports.
pub fn PassageKind::code(self : PassageKind) -> String {
match self {
Door => "door"
Hallway => "hallway"
Stairway => "stairway"
Ramp => "ramp"
Outdoor => "outdoor"
}
}
///|
/// Stable textual name used in reports.
pub fn Severity::code(self : Severity) -> String {
match self {
Info => "info"
Warning => "warning"
Error => "error"
}
}
///|
/// Stable textual name used in reports.
pub fn Mobility::code(self : Mobility) -> String {
match self {
Fast => "fast"
Standard => "standard"
Slow => "slow"
Assisted => "assisted"
}
}
///|
/// Find a place index using a deterministic linear scan.
pub fn EvacuationPlan::place_index(self : EvacuationPlan, id : String) -> Int {
for i = 0; i < self.places.length(); i = i + 1 {
if self.places[i].id == id {
return i
}
}
-1
}
///|
/// Find a passage index using a deterministic linear scan.
pub fn EvacuationPlan::passage_index(self : EvacuationPlan, id : String) -> Int {
for i = 0; i < self.passages.length(); i = i + 1 {
if self.passages[i].id == id {
return i
}
}
-1
}
///|
/// Return all exit indices.
pub fn EvacuationPlan::exit_indices(self : EvacuationPlan) -> Array[Int] {
let result : Array[Int] = []
for i = 0; i < self.places.length(); i = i + 1 {
if self.places[i].kind == Exit {
result.push(i)
}
}
result
}
///|
/// Total number of people in the plan.
pub fn EvacuationPlan::population_count(self : EvacuationPlan) -> Int {
let mut total = 0
for group in self.populations {
total = total + group.count
}
total
}
///|
/// Number of locations of a given type.
pub fn EvacuationPlan::place_kind_count(
self : EvacuationPlan,
kind : PlaceKind,
) -> Int {
let mut total = 0
for place in self.places {
if place.kind == kind {
total = total + 1
}
}
total
}
///|
/// Create a diagnostic value.
pub fn diagnostic(
code : String,
severity : Severity,
subject : String,
message : String,
hint : String,
) -> Diagnostic {
{ code, severity, subject, message, hint }
}