Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
73 changes: 73 additions & 0 deletions sim/src/checker/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@

use std::collections::BTreeSet;
use crate::simulator::Simulation;
use crate::utils::errors::{SimulationError, SimulationResult};

/// Provide tools to check a simulation and verify that the
/// models and connections within it are 'correct'
///
///

pub trait Checker {
fn connectors_source_to_model(&self) -> SimulationResult<()>;
fn connectors_target_to_model(&self) -> SimulationResult<()>;

/// Collect up a list of unique model ids.
fn unique_model_ids(&self) -> SimulationResult<()>;

fn valid_messages(&self) -> SimulationResult<()>;

fn check(&self) -> SimulationResult<()>;
}

impl Checker for Simulation {
fn check(&self) -> SimulationResult<()> {
//Check all of the contained checks. if any return an error result then bail.
self.connectors_source_to_model()
.and(self.connectors_target_to_model())
.and(self.valid_messages())
.and(self.unique_model_ids())
}

fn connectors_source_to_model(&self) -> SimulationResult<()> {
self.get_connectors().iter().try_for_each(|connector| {
match self.get_model(connector.source_id()) {
Some(_) => Ok(()),
None => Err(SimulationError::InvalidModelConfiguration),
}
})
}

fn connectors_target_to_model(&self) -> SimulationResult<()> {
self.get_connectors().iter().try_for_each(|connector| {
match self.get_model(connector.target_id()) {
Some(_) => Ok(()),
None => Err(SimulationError::InvalidModelConfiguration),
}
})
}

///Any initial messages should have a target_id that matches a model node.
fn valid_messages(&self) -> SimulationResult<()> {
self.get_messages()
.iter()
.try_for_each(
|connector| match self.get_model(connector.target_id()) {
Some(_) => Ok(()),
None => Err(SimulationError::InvalidMessage),
},
)
}

/// Throw an error if a model id is used more than once.
fn unique_model_ids(&self) -> SimulationResult<()> {
let model_count: usize = self.get_models().len();
let items: BTreeSet<&str> = self.get_models()
.iter().map(|m| m.id()).collect();

match model_count == items.len() {
true => Ok(()),
false => { Err(SimulationError::InvalidModelConfiguration) }
}
}
}
3 changes: 3 additions & 0 deletions sim/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,3 +14,6 @@ pub mod models;
pub mod output_analysis;
pub mod simulator;
pub mod utils;

pub mod checker;
pub mod report;
32 changes: 32 additions & 0 deletions sim/src/report/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
use crate::simulator::{Simulation};

pub trait Report {
fn generate_dot_graph(&self) -> String;
}

impl Report for Simulation {
fn generate_dot_graph(&self) -> String {
let models = self.get_models();
let connectors = self.get_connectors();

let mut dot_string = String::from("digraph DAG {\n");

// Add nodes
for model in models {
dot_string.push_str(&format!(" \"{}\" [shape=box];\n", model.id()));
}

// Add edges
for connector in connectors {
dot_string.push_str(&format!(
" \"{}\" -> \"{}\" [label=\"{}\"];\n",
connector.source_id(),
connector.target_id(),
connector.id()
));
}

dot_string.push_str("}\n");
dot_string
}
}
4 changes: 4 additions & 0 deletions sim/src/simulator/coupling.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,10 @@ impl Connector {
}
}

pub fn id(&self) -> &str {
&self.id
}

/// This accessor method returns the model ID of the connector source model.
pub fn source_id(&self) -> &str {
&self.source_id
Expand Down
14 changes: 14 additions & 0 deletions sim/src/simulator/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,20 @@ impl Simulation {
self.models.iter_mut().collect()
}

/// Provide immutable reference to models for analysis. Can't change. Just look.
pub fn get_models(&self) -> &[Model] {
&self.models
}

/// find a specific model by id.
pub fn get_model(&self, model_id: &str) -> Option<&Model> {
self.models.iter().find(|model| model.id() == model_id)
}

pub fn get_connectors(&self) -> &[Connector] {
&self.connectors
}

/// This method constructs a list of target IDs for a given source model
/// ID and port. This message target information is derived from the
/// connectors configuration.
Expand Down
3 changes: 3 additions & 0 deletions sim/src/utils/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -95,3 +95,6 @@ pub enum SimulationError {
#[error(transparent)]
WeightedError(#[from] rand_distr::WeightedError),
}

// Define a generic alias for a `Result` with the error type `SimulationError`.
pub type SimulationResult<T> = Result<T, SimulationError>;