use crate::customer::Customer;
use crate::event::{Event, EventType};
use crate::server::{Server, ServerStatus};
use crate::simulation::{
observation::Observation, params::SimParams, service_metrics::ServiceMetrics,
};
use rand::prelude::*;
use rand_distr::Exp;
use std::cmp::Reverse;
use std::collections::BinaryHeap;
pub struct SimEnv {
event_queue: BinaryHeap<Event>,
customers: BinaryHeap<Customer>,
t: f64,
params: SimParams,
server: Server,
queue: BinaryHeap<Reverse<Customer>>,
pub history: Vec<Observation>,
service_metrics: ServiceMetrics,
n_served: usize,
}
impl SimEnv {
pub fn new(params: &SimParams) -> SimEnv {
let sim = SimEnv {
event_queue: BinaryHeap::new(),
customers: BinaryHeap::new(),
t: 0.0,
params: *params,
server: Server {
status: ServerStatus::Idling,
},
queue: BinaryHeap::new(),
history: Vec::new(),
service_metrics: ServiceMetrics::new(),
n_served: 0 as usize,
};
sim
}
pub fn simulate(&mut self, until: f64) {
let first_arrival_time = self.t
+ Exp::new(1.0 / self.params.mean_inter_arrival_time)
.unwrap()
.sample(&mut thread_rng());
let service_time = Exp::new(1.0 / self.params.mean_service_time)
.unwrap()
.sample(&mut thread_rng());
let c = Customer::new(first_arrival_time, service_time);
self.customers.push(c);
let e = Event::new(EventType::Arrival, c, first_arrival_time);
self.event_queue.push(e);
self.t = first_arrival_time;
while self.t < until {
let n_in_service = match self.server.status {
ServerStatus::Idling => 0 as usize,
ServerStatus::Busy => 1 as usize,
};
let obs = Observation {
t: self.t,
n_in_queue: self.queue.len(),
n_in_system: self.queue.len() + n_in_service,
};
self.history.push(obs);
let next_event = self.event_queue.pop();
match next_event {
None => {
return;
}
Some(mut e) => {
match e.event_type {
EventType::Arrival => {
self.handle_arrival(&e);
}
EventType::StartService => {
self.handle_start_service(&mut e);
}
EventType::Departure => {
self.handle_end_service(&mut e);
}
};
self.t = e.t;
}
}
}
}
fn handle_arrival(&mut self, event: &Event) {
match self.server.status {
ServerStatus::Busy => {
self.queue.push(Reverse(event.get_customer()));
}
ServerStatus::Idling => {
let start_event =
Event::new(EventType::StartService, event.get_customer(), event.t);
self.event_queue.push(start_event);
}
}
let next_inter_arrival_time = Exp::new(1.0 / self.params.mean_inter_arrival_time)
.unwrap()
.sample(&mut thread_rng());
let next_service_time = Exp::new(1.0 / self.params.mean_service_time)
.unwrap()
.sample(&mut thread_rng());
let new_customer = Customer::new(
event.get_customer().arrival_time + next_inter_arrival_time,
next_service_time,
);
self.event_queue.push(Event::new(
EventType::Arrival,
new_customer,
new_customer.arrival_time,
))
}
fn handle_start_service(&mut self, event: &mut Event) {
let t = event.t;
event.get_customer_mut().set_service_start(t);
let end_event = Event::new(
EventType::Departure,
event.get_customer(),
t + event.get_customer().service_time,
);
self.event_queue.push(end_event);
self.server.status = ServerStatus::Busy;
}
fn handle_end_service(&mut self, event: &mut Event) {
let t = event.t;
let served_customer = event.get_customer_mut();
served_customer.set_service_end(t);
let w_q = served_customer.waiting_time().unwrap();
let w_s = served_customer.cycle_time().unwrap();
match (
self.service_metrics.mean_waiting_time,
self.service_metrics.mean_cycle_time,
) {
(None, None) => {
self.service_metrics.mean_cycle_time = Some(w_s);
self.service_metrics.mean_waiting_time = Some(w_q);
}
_ => {
self.service_metrics.mean_waiting_time = Some(
(self.service_metrics.mean_waiting_time.unwrap() * self.n_served as f64 + w_q)
/ (self.n_served as f64 + 1.0),
);
self.service_metrics.mean_cycle_time = Some(
(self.service_metrics.mean_cycle_time.unwrap() * self.n_served as f64 + w_s)
/ (self.n_served as f64 + 1.0),
);
}
}
self.n_served += 1;
let next_customer = self.queue.pop();
self.server.status = ServerStatus::Idling;
match next_customer {
Some(c) => {
let e = Event::new(EventType::StartService, c.0, event.t);
self.event_queue.push(e);
}
None => {}
}
}
pub fn get_metrics(&self) -> ServiceMetrics {
self.service_metrics
}
}