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#[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 {}