use std::f64::INFINITY;
use serde::{Deserialize, Serialize};
use super::AsModel;
use super::ModelMessage;
use crate::simulator::Services;
use crate::utils::error::SimulationError;
use crate::utils::{populate_history_port, populate_snapshot_port};
#[derive(Debug, Clone, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct Gate {
ports_in: PortsIn,
ports_out: PortsOut,
#[serde(default)]
state: State,
#[serde(default)]
snapshot: Metrics,
#[serde(default)]
history: Vec<Metrics>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
struct PortsIn {
job: String,
activation: String,
deactivation: String,
snapshot: Option<String>,
history: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
struct PortsOut {
job: String,
snapshot: Option<String>,
history: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
struct State {
event_list: Vec<ScheduledEvent>,
jobs: Vec<String>,
phase: Phase,
}
impl Default for State {
fn default() -> Self {
let initalization_event = ScheduledEvent {
time: 0.0,
event: Event::Run,
};
State {
event_list: vec![initalization_event],
jobs: Vec::new(),
phase: Phase::Open,
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
struct ScheduledEvent {
time: f64,
event: Event,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
enum Event {
Run,
DropJob,
SendJob,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
enum Phase {
Open,
Closed,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
struct Metrics {
last_received: Option<(String, f64)>,
last_activation: Option<f64>,
last_deactivation: Option<f64>,
pass_count: usize,
block_count: usize,
is_open: Option<bool>,
}
impl Default for Metrics {
fn default() -> Self {
Metrics {
last_received: None,
last_activation: None,
last_deactivation: None,
pass_count: 0,
block_count: 0,
is_open: None,
}
}
}
impl Gate {
pub fn new(
job_in_port: String,
activation_port: String,
deactivation_port: String,
job_out_port: String,
snapshot_metrics: bool,
history_metrics: bool,
) -> Self {
Self {
ports_in: PortsIn {
job: job_in_port,
activation: activation_port,
deactivation: deactivation_port,
snapshot: populate_snapshot_port(snapshot_metrics),
history: populate_history_port(history_metrics),
},
ports_out: PortsOut {
job: job_out_port,
snapshot: populate_snapshot_port(snapshot_metrics),
history: populate_history_port(history_metrics),
},
state: Default::default(),
snapshot: Default::default(),
history: Default::default(),
}
}
fn need_snapshot_metrics(&self) -> bool {
self.ports_in.snapshot.is_some() && self.ports_out.snapshot.is_some()
}
fn need_historical_metrics(&self) -> bool {
self.need_snapshot_metrics()
&& self.ports_in.history.is_some()
&& self.ports_out.history.is_some()
}
}
impl AsModel for Gate {
fn status(&self) -> String {
match self.state.phase {
Phase::Open => String::from("Listening"),
Phase::Closed => String::from("Blocked"),
}
}
fn events_ext(
&mut self,
incoming_message: ModelMessage,
services: &mut Services,
) -> Result<Vec<ModelMessage>, SimulationError> {
let incoming_port: &str = &incoming_message.port_name;
match &self.ports_in {
PortsIn { activation, .. } if activation == incoming_port => {
if self.need_snapshot_metrics() {
self.snapshot.last_activation = Some(services.global_time());
self.snapshot.is_open = Some(true);
}
if self.need_historical_metrics() {
self.history.push(self.snapshot.clone());
}
self.state.phase = Phase::Open;
}
PortsIn { deactivation, .. } if deactivation == incoming_port => {
if self.need_snapshot_metrics() {
self.snapshot.last_deactivation = Some(services.global_time());
self.snapshot.is_open = Some(false);
}
if self.need_historical_metrics() {
self.history.push(self.snapshot.clone());
}
self.state.phase = Phase::Closed;
}
PortsIn { job, .. } if job == incoming_port => {
if self.need_snapshot_metrics() {
self.snapshot.last_received =
Some((incoming_message.content.clone(), services.global_time()));
}
if self.need_historical_metrics() {
self.history.push(self.snapshot.clone());
}
self.state.jobs.push(incoming_message.content);
match self.state.phase {
Phase::Closed => self.state.event_list.push(ScheduledEvent {
time: 0.0,
event: Event::DropJob,
}),
Phase::Open => self.state.event_list.push(ScheduledEvent {
time: 0.0,
event: Event::SendJob,
}),
}
}
_ => return Err(SimulationError::PortNotFound),
}
Ok(Vec::new())
}
fn events_int(
&mut self,
_services: &mut Services,
) -> Result<Vec<ModelMessage>, SimulationError> {
let mut outgoing_messages: Vec<ModelMessage> = Vec::new();
let events = self.state.event_list.clone();
self.state.event_list = self
.state
.event_list
.iter()
.filter(|scheduled_event| scheduled_event.time != 0.0)
.cloned()
.collect();
events
.iter()
.filter(|scheduled_event| scheduled_event.time == 0.0)
.for_each(|scheduled_event| match scheduled_event.event {
Event::Run => {}
Event::DropJob => {
if self.need_snapshot_metrics() {
self.snapshot.block_count += 1;
}
if self.need_historical_metrics() {
self.history.push(self.snapshot.clone());
}
self.state.jobs.remove(0);
}
Event::SendJob => {
if self.need_snapshot_metrics() {
self.snapshot.pass_count += 1;
}
if self.need_historical_metrics() {
self.history.push(self.snapshot.clone());
}
outgoing_messages.push(ModelMessage {
port_name: self.ports_out.job.clone(),
content: self.state.jobs.remove(0),
});
}
});
Ok(outgoing_messages)
}
fn time_advance(&mut self, time_delta: f64) {
self.state
.event_list
.iter_mut()
.for_each(|scheduled_event| {
scheduled_event.time -= time_delta;
});
}
fn until_next_event(&self) -> f64 {
self.state
.event_list
.iter()
.fold(INFINITY, |until_next_event, event| {
f64::min(until_next_event, event.time)
})
}
}