/* Copyright © 2021 Gianmarco Garrisi This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ //! This example shows how to create a custom resource and implement the resource trait. //! //! Features shown in this example: //! * Custom Resources //! * Prelude //! * Custom state //! * EndCondition::NoEvents #![feature(coroutines)] use desim::prelude::*; use desim::resources::Resource; use rand::{ distributions::{Distribution, Uniform}, rngs::SmallRng as Rng, SeedableRng, }; const Q_SIZE: usize = 30; const NUM_CLIENTS: usize = 800; const SIM_TIME: f64 = 50.0; #[derive(Copy, Clone, Debug)] struct State { effect: Effect, queue_full: bool, } impl State { fn new(effect: Effect) -> State { State { effect, queue_full: false, } } } struct FiniteQueue { _quantity: usize, available: usize, queue: [Option>; Q_SIZE], queue_start: usize, queue_len: usize, } impl SimState for State { fn get_effect(&self) -> Effect { self.effect } fn set_effect(&mut self, effect: Effect) { self.effect = effect; } fn should_log(&self) -> bool { true } } impl Resource for FiniteQueue { fn allocate_or_enqueue(&mut self, event: Event) -> Option> { if self.available > 0 { self.available -= 1; Some(event) } else if self.queue_len == Q_SIZE { let mut event = event; event.state_mut().queue_full = true; Some(event) } else { let first_position = (self.queue_start + self.queue_len) % Q_SIZE; self.queue[first_position] = Some(event); self.queue_len += 1; None } } fn release_and_schedule_next(&mut self, event: Event) -> Option> { if self.queue_len > 0 { let mut next_event = self.queue[self.queue_start].take().unwrap(); self.queue_start = (self.queue_start + 1) % Q_SIZE; self.queue_len -= 1; next_event.set_time(event.time()); Some(next_event) } else { self.available += 1; None } } } fn client_process(res: ResourceId) -> Box> { Box::new(move |response: SimContext| { yield State::new(Effect::Request(res)); if !response.state().queue_full { yield State::new(Effect::TimeOut(5.0)); yield State::new(Effect::Release(res)); } else { yield State::new(Effect::Trace); } }) } fn main() { let mut sim = Simulation::new(); let unif = Uniform::new(0.0, SIM_TIME); let rng = Rng::from_entropy(); let res = FiniteQueue { _quantity: 4, available: 4, queue: [None; Q_SIZE], queue_start: 0, queue_len: 0, }; let res = sim.create_resource(Box::new(res)); // Create NUM_CLIENTS processes and schedule them at random times for t in unif.sample_iter(rng).take(NUM_CLIENTS) { let p = sim.create_process(client_process(res)); sim.schedule_event(t, p, State::new(Effect::TimeOut(0.0))); } sim = sim.run(EndCondition::NoEvents); // Print the simulation log for (e, state) in sim.processed_events() { println!("{}\t{:?}\t{:?}", e.time(), e.state(), state); } println!( "Lost clients: {}", sim.processed_events() .iter() .filter(|(e, _)| e.state().queue_full) .count() ); }