sim/models/
gate.rs

1use std::f64::INFINITY;
2
3use serde::{Deserialize, Serialize};
4
5use super::model_trait::{DevsModel, Reportable, ReportableModel, SerializableModel};
6use super::{ModelMessage, ModelRecord};
7use crate::simulator::Services;
8use crate::utils::errors::SimulationError;
9
10use sim_derive::SerializableModel;
11
12#[cfg(feature = "simx")]
13use simx::event_rules;
14
15/// The gate model passes or blocks jobs, when it is in the open or closed
16/// state, respectively. The gate can be opened and closed throughout the
17/// course of a simulation. This model contains no stochastic behavior - job
18/// passing/blocking is based purely on the state of the model at that time
19/// in the simulation. A blocked job is a dropped job - it is not stored,
20/// queued, or redirected.
21#[derive(Debug, Clone, Serialize, Deserialize, SerializableModel)]
22#[serde(rename_all = "camelCase")]
23pub struct Gate {
24    ports_in: PortsIn,
25    ports_out: PortsOut,
26    #[serde(default)]
27    store_records: bool,
28    #[serde(default)]
29    state: State,
30}
31
32#[derive(Debug, Clone, Serialize, Deserialize)]
33struct PortsIn {
34    job: String,
35    activation: String,
36    deactivation: String,
37}
38
39#[derive(Debug, Clone, Serialize, Deserialize)]
40enum ArrivalPort {
41    Job,
42    Activation,
43    Deactivation,
44    Unknown,
45}
46
47#[derive(Debug, Clone, Serialize, Deserialize)]
48struct PortsOut {
49    job: String,
50}
51
52#[derive(Debug, Clone, Serialize, Deserialize)]
53#[serde(rename_all = "camelCase")]
54struct State {
55    phase: Phase,
56    until_next_event: f64,
57    jobs: Vec<String>,
58    records: Vec<ModelRecord>,
59}
60
61impl Default for State {
62    fn default() -> Self {
63        Self {
64            phase: Phase::Open,
65            until_next_event: INFINITY,
66            jobs: Vec::new(),
67            records: Vec::new(),
68        }
69    }
70}
71
72#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
73enum Phase {
74    Open,
75    Closed,
76    Pass,
77}
78
79#[cfg_attr(feature = "simx", event_rules)]
80impl Gate {
81    pub fn new(
82        job_in_port: String,
83        activation_port: String,
84        deactivation_port: String,
85        job_out_port: String,
86        store_records: bool,
87    ) -> Self {
88        Self {
89            ports_in: PortsIn {
90                job: job_in_port,
91                activation: activation_port,
92                deactivation: deactivation_port,
93            },
94            ports_out: PortsOut { job: job_out_port },
95            store_records,
96            state: State::default(),
97        }
98    }
99
100    fn arrival_port(&self, message_port: &str) -> ArrivalPort {
101        if message_port == self.ports_in.job {
102            ArrivalPort::Job
103        } else if message_port == self.ports_in.activation {
104            ArrivalPort::Activation
105        } else if message_port == self.ports_in.deactivation {
106            ArrivalPort::Deactivation
107        } else {
108            ArrivalPort::Unknown
109        }
110    }
111
112    fn activate(&mut self, incoming_message: &ModelMessage, services: &mut Services) {
113        self.state.phase = Phase::Open;
114        self.state.until_next_event = INFINITY;
115        self.record(
116            services.global_time(),
117            String::from("Activation"),
118            incoming_message.content.clone(),
119        );
120    }
121
122    fn deactivate(&mut self, incoming_message: &ModelMessage, services: &mut Services) {
123        self.state.phase = Phase::Closed;
124        self.state.until_next_event = INFINITY;
125        self.record(
126            services.global_time(),
127            String::from("Deactivation"),
128            incoming_message.content.clone(),
129        );
130    }
131
132    fn pass_job(&mut self, incoming_message: &ModelMessage, services: &mut Services) {
133        self.state.phase = Phase::Pass;
134        self.state.until_next_event = 0.0;
135        self.state.jobs.push(incoming_message.content.clone());
136        self.record(
137            services.global_time(),
138            String::from("Arrival"),
139            incoming_message.content.clone(),
140        );
141    }
142
143    fn drop_job(&mut self, incoming_message: &ModelMessage, services: &mut Services) {
144        self.record(
145            services.global_time(),
146            String::from("Arrival"),
147            incoming_message.content.clone(),
148        );
149    }
150
151    fn send_jobs(&mut self, services: &mut Services) -> Vec<ModelMessage> {
152        self.state.phase = Phase::Open;
153        self.state.until_next_event = INFINITY;
154        (0..self.state.jobs.len())
155            .map(|_| {
156                self.record(
157                    services.global_time(),
158                    String::from("Departure"),
159                    self.state.jobs[0].clone(),
160                );
161                ModelMessage {
162                    port_name: self.ports_out.job.clone(),
163                    content: self.state.jobs.remove(0),
164                }
165            })
166            .collect()
167    }
168
169    fn record(&mut self, time: f64, action: String, subject: String) {
170        if self.store_records {
171            self.state.records.push(ModelRecord {
172                time,
173                action,
174                subject,
175            });
176        }
177    }
178}
179
180#[cfg_attr(feature = "simx", event_rules)]
181impl DevsModel for Gate {
182    fn events_ext(
183        &mut self,
184        incoming_message: &ModelMessage,
185        services: &mut Services,
186    ) -> Result<(), SimulationError> {
187        match (
188            self.arrival_port(&incoming_message.port_name),
189            self.state.phase == Phase::Closed,
190        ) {
191            (ArrivalPort::Activation, _) => Ok(self.activate(incoming_message, services)),
192            (ArrivalPort::Deactivation, _) => Ok(self.deactivate(incoming_message, services)),
193            (ArrivalPort::Job, false) => Ok(self.pass_job(incoming_message, services)),
194            (ArrivalPort::Job, true) => Ok(self.drop_job(incoming_message, services)),
195            (ArrivalPort::Unknown, _) => Err(SimulationError::InvalidMessage),
196        }
197    }
198
199    fn events_int(
200        &mut self,
201        services: &mut Services,
202    ) -> Result<Vec<ModelMessage>, SimulationError> {
203        Ok(self.send_jobs(services))
204    }
205
206    fn time_advance(&mut self, time_delta: f64) {
207        self.state.until_next_event -= time_delta;
208    }
209
210    fn until_next_event(&self) -> f64 {
211        self.state.until_next_event
212    }
213}
214
215impl Reportable for Gate {
216    fn status(&self) -> String {
217        match self.state.phase {
218            Phase::Open => String::from("Open"),
219            Phase::Closed => String::from("Closed"),
220            Phase::Pass => format!["Passing {}", self.state.jobs[0]],
221        }
222    }
223
224    fn records(&self) -> &Vec<ModelRecord> {
225        &self.state.records
226    }
227}
228
229impl ReportableModel for Gate {}