use core::{cell::Cell, pin::pin};
use odem_rs::{
prelude::*,
sync::{Subscriber, chain::Chain},
};
use rand_distr::Exp;
use tracing::{Level, debug, info};
#[derive(Config, Default)]
#[time(Time<f64>)]
#[rank(u32 = 0)]
struct Caltech {
rng_stream: RngStream,
floor: Cell<usize>,
d1: Control<bool>,
cabin: Chain,
callup: [Control<bool>; 5],
calldown: [Control<bool>; 5],
callcar: [Control<bool>; 5],
}
impl Caltech {
fn is_going_down(&self) -> bool {
for j in 0..self.floor.get() {
if self.callup[j].get() || self.calldown[j].get() || self.callcar[j].get() {
return true;
}
}
false
}
fn is_going_up(&self) -> bool {
for j in self.floor.get() + 1..5 {
if self.callup[j].get() || self.calldown[j].get() || self.callcar[j].get() {
return true;
}
}
false
}
async fn move_down(&self, sim: &Sim<Self>) {
info!("Elevator moving down.");
self.floor.set(self.floor.get() - 1);
sim.advance(second::new(6.1)).await;
}
async fn move_up(&self, sim: &Sim<Self>) {
info!("Elevator moving up.");
self.floor.set(self.floor.get() + 1);
sim.advance(second::new(5.1)).await;
}
}
#[odem_rs::main]
#[allow(dead_code)]
async fn sim_main(sim: &Sim<Caltech>, duration: Time<f64>) {
let passengers = pin!(Pool::fixed::<10>());
let arrivals = pin!(Job::new(async {
let intertime = Exp::new(0.5).unwrap();
let rng = &mut sim.global().rng_stream.rng();
loop {
sim.advance(minute::new(rng.sample(intertime))).await;
sim.activate(passengers.alloc({
let in_floor = rng.random_range(0..5);
let out_floor = rng.random_range(0..4);
let out_floor = out_floor + (out_floor >= in_floor) as usize;
let giveuptime = second::new(rng.random_range(15.0..30.0));
Agent::build()
.with_subject(Passenger {
in_floor,
out_floor,
giveuptime,
})
.with_actions(Passenger::actions)
.with_name("Man")
.with_rank(1)
.finish()
}));
}
}));
let elevator = pin!(Agent::new(Elevator));
sim.activate(arrivals);
sim.activate(elevator);
sim.advance(duration).await
}
#[odem_rs::main]
#[allow(dead_code)]
async fn knuth_sim(sim: &Sim<Caltech>) {
let passengers = pin!(Pool::fixed::<11>());
let arrivals = [
(
second::new(0.0),
Passenger {
in_floor: 0,
out_floor: 2,
giveuptime: second::new(15.2),
},
),
(
second::new(3.8),
Passenger {
in_floor: 4,
out_floor: 1,
giveuptime: second::new(f64::INFINITY),
},
),
(
second::new(13.6),
Passenger {
in_floor: 2,
out_floor: 1,
giveuptime: second::new(f64::INFINITY),
},
),
(
second::new(14.1),
Passenger {
in_floor: 2,
out_floor: 1,
giveuptime: second::new(f64::INFINITY),
},
),
(
second::new(29.1),
Passenger {
in_floor: 3,
out_floor: 1,
giveuptime: second::new(f64::INFINITY),
},
),
(
second::new(36.4),
Passenger {
in_floor: 2,
out_floor: 1,
giveuptime: second::new(17.6),
},
),
(
second::new(60.2),
Passenger {
in_floor: 1,
out_floor: 2,
giveuptime: second::new(f64::INFINITY),
},
),
(
second::new(82.7),
Passenger {
in_floor: 1,
out_floor: 0,
giveuptime: second::new(f64::INFINITY),
},
),
(
second::new(87.6),
Passenger {
in_floor: 1,
out_floor: 3,
giveuptime: second::new(f64::INFINITY),
},
),
(
second::new(104.8),
Passenger {
in_floor: 0,
out_floor: 4,
giveuptime: second::new(f64::INFINITY),
},
),
(
second::new(438.4),
Passenger {
in_floor: 2,
out_floor: 3,
giveuptime: second::new(f64::INFINITY),
},
),
];
for (time, passenger) in arrivals {
sim.schedule(
passengers.alloc(
Agent::build()
.with_subject(passenger)
.with_actions(Passenger::actions)
.with_name("Man")
.with_rank(1)
.finish(),
),
time,
);
}
let elevator = pin!(Agent::new(Elevator));
sim.schedule(elevator, second::new(2.0));
sim.advance(second::new(1000.0)).await
}
struct Passenger {
in_floor: usize,
out_floor: usize,
giveuptime: Time<f64>,
}
impl Behavior<Caltech> for Passenger {
type Output = ();
async fn actions(&self, sim: &Sim<Caltech>) {
let g = sim.global();
let id = sim.active().label().pid.unwrap();
debug!(
"Man no. {id} arrives at floor {}, destination is {}.",
self.in_floor, self.out_floor
);
let call_button = if self.in_floor > self.out_floor {
&g.calldown[self.in_floor]
} else {
&g.callup[self.in_floor]
};
call_button.set(true);
if sim
.fork(async {
until!(!call_button).await;
false
})
.or(async {
loop {
sim.advance(self.giveuptime).await;
if g.floor.get() != self.in_floor || !g.d1.get() {
break true;
}
}
})
.await
{
debug!("Man no. {id} decides to give up and leaves.");
return;
}
until!(!g.d1).await;
g.d1.set(true);
debug!("Man no. {id} gets in.");
sim.advance(second::new(2.5)).await;
g.d1.set(false);
g.callcar[self.out_floor].set(true);
while g.floor.get() != self.out_floor {
g.cabin.lifo().await;
}
until!(!g.d1).await;
g.d1.set(true);
debug!("Man no. {id} gets out, leaves system.");
sim.advance(second::new(2.5)).await;
g.d1.set(false);
}
}
struct Elevator;
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
enum ElevatorState {
Neutral,
GoingUp,
GoingDown,
}
impl Behavior<Caltech> for Elevator {
type Output = ();
async fn actions(&self, sim: &Sim<Caltech>) {
let g = sim.global();
g.floor.set(2);
let state = Cell::new(ElevatorState::Neutral);
let door_open = Cell::new(false);
let close_door = Control::new(false);
let e5 = pin!(Job::new(async {
loop {
until!(close_door).await;
sim.fork(sim.advance(second::new(7.6)))
.or(async {
if state.get() != ElevatorState::Neutral {
sleep().await;
}
until!(g.d1).await;
sim.advance(second::new(2.5)).await;
})
.await;
while sim
.fork(async {
until!(g.d1).await;
true
})
.or(async {
info!("Elevator door starts to close.");
sim.advance(second::new(2.0)).await;
door_open.set(false);
close_door.set(false);
false
})
.await
{
info!("Doors flutter.");
sim.advance(second::new(4.0)).await;
}
}
}));
let check_inaction = Control::new(false);
let e9 = pin!(Job::new(async {
loop {
until!(check_inaction).await;
if sim
.fork(async {
sim.advance(second::new(30.0)).await;
true
})
.or(async {
until!(!check_inaction).await;
false
})
.await
{
check_inaction.set(false);
if g.floor.get() == 2 {
info!("Elevator dormant.");
continue;
}
g.callcar[2].set(true);
}
}
}));
sim.activate(e5);
sim.activate(e9);
let check = async |floor: usize| {
until!(g.callup[floor] || g.calldown[floor] || g.callcar[floor]).await;
};
loop {
match state.get() {
ElevatorState::Neutral => {}
ElevatorState::GoingUp => {
if g.callup[g.floor.get()].get() {
g.callup[g.floor.get()].set(false);
}
if !g.is_going_up() {
state.set(ElevatorState::Neutral);
}
}
ElevatorState::GoingDown => {
if g.calldown[g.floor.get()].get() {
g.calldown[g.floor.get()].set(false);
}
if !g.is_going_down() {
state.set(ElevatorState::Neutral);
}
}
}
sim.fork(check(0))
.or(check(1))
.or(check(2))
.or(check(3))
.or(check(4))
.await;
if state.get() == ElevatorState::Neutral {
state.set(
if g.callup[g.floor.get()].get() || g.calldown[g.floor.get()].get() {
if !door_open.get() {
close_door.set(true);
info!("Elevator door starts to open.");
sim.advance(second::new(2.0)).await;
door_open.set(true);
}
if g.calldown[g.floor.get()].get() {
g.calldown[g.floor.get()].set(false);
} else {
g.callup[g.floor.get()].set(false);
}
continue;
} else if g.is_going_down() {
ElevatorState::GoingDown
} else {
ElevatorState::GoingUp
},
);
}
if door_open.get() {
until!(!close_door).await;
}
check_inaction.set(false);
match state.get() {
ElevatorState::Neutral => unreachable!(),
ElevatorState::GoingUp => {
sim.advance(second::new(1.5)).await;
while g.is_going_up() {
g.move_up(sim).await;
if g.callcar[g.floor.get()].get() || g.callup[g.floor.get()].get() {
break;
}
}
sim.advance(second::new(1.4)).await;
if !g.is_going_up() {
state.set(ElevatorState::Neutral);
}
}
ElevatorState::GoingDown => {
sim.advance(second::new(1.5)).await;
while g.is_going_down() {
g.move_down(sim).await;
if g.callcar[g.floor.get()].get() || g.calldown[g.floor.get()].get() {
break;
}
}
sim.advance(second::new(2.3)).await;
if !g.is_going_down() {
state.set(ElevatorState::Neutral);
}
}
}
info!("Elevator stops in floor {}.", g.floor.get());
g.callcar[g.floor.get()].set(false);
close_door.set(true);
check_inaction.set(true);
info!("Elevator door starts to open.");
sim.advance(second::new(2.0)).await;
door_open.set(true);
g.cabin.notify();
}
}
}
fn main() {
tracing_subscriber::fmt()
.with_max_level(Level::DEBUG)
.with_target(false)
.with_timer(model_time!("[{time:5.1}]"))
.compact()
.init();
knuth_sim();
}