mosaik_api/
lib.rs

1pub mod mosaik_protocol;
2pub mod tcp;
3pub mod types;
4
5use crate::{
6    tcp::{build_connection, ConnectionDirection},
7    types::{Attr, CreateResult, InputData, Meta, OutputData, OutputRequest, SimId, Time},
8};
9
10use async_std::task;
11use async_trait::async_trait;
12#[cfg(test)]
13use mockall::automock;
14use serde_json::{Map, Value};
15
16type AResult<T> = std::result::Result<T, Box<dyn std::error::Error + Send + Sync>>;
17
18///Main calls this function with the simulator that should run and the direction with an address for the connection to mosaik.
19pub fn run_simulation<T: MosaikApi>(addr: ConnectionDirection, simulator: T) -> AResult<()> {
20    task::block_on(build_connection(addr, simulator))
21}
22
23/// The `MosaikApi` trait defines the interface for a Mosaik simulator API.
24/// Errors will result in a Failure Response being sent to Mosaik containing the Error's message and/or Stack Trace.
25#[cfg_attr(test, automock)]
26pub trait MosaikApi: Send + 'static {
27    /// Initialize the simulator with the specified ID (`sid`), time resolution (`time_resolution`), and additional parameters (`sim_params`).
28    /// Returns the meta data (`Meta`) of the simulator.
29    fn init(
30        &mut self,
31        sid: SimId,
32        time_resolution: f64,
33        sim_params: Map<String, Value>,
34    ) -> Result<&'static Meta, String>;
35
36    /// Create `num` instances of the specified `model_name` using the provided `model_params`.
37    /// The returned list must have the same length as `num`.
38    fn create(
39        &mut self,
40        num: usize,
41        model_name: String,
42        model_params: Map<Attr, Value>,
43    ) -> Result<Vec<CreateResult>, String>;
44
45    /// This function is called by Mosaik when the `init()` and `create()` calls are done.
46    /// Returns `Null`.
47    fn setup_done(&self) -> Result<(), String>;
48
49    /// Perform the next simulation step at `time` and return the new simulation time (the time at which `step` should be called again),
50    /// or `None` if the simulator doesn't need to step itself. The Return value must be set for time-based simulators.
51    fn step(
52        &mut self,
53        time: Time,
54        inputs: InputData,
55        max_advance: Time,
56    ) -> Result<Option<Time>, String>;
57
58    /// Collect data from the simulation and return a nested vector (`OutputData`) containing the information.
59    fn get_data(&self, outputs: OutputRequest) -> Result<OutputData, String>;
60
61    /// This function is called by Mosaik when the simulation is finished.
62    /// Returns `Null`. The simulation API stops as soon as the function returns.
63    fn stop(&self);
64
65    /// A wrapper for extra methods that can be implemented by the simulator.
66    /// This method is not required by the Mosaik API, but can be used for additional functionality.
67    /// Returns a `Result` containing the result of the method call or an error message if the method is not found.
68    fn extra_method(
69        &mut self,
70        method: &str,
71        args: &Vec<Value>,
72        kwargs: &Map<String, Value>,
73    ) -> Result<Value, String> {
74        Err(format!(
75            "Method '{method}' not found with args: {args:?} and kwargs: {kwargs:?}"
76        ))
77    }
78}
79
80///Async API calls, not implemented!
81#[allow(dead_code)]
82#[async_trait]
83trait AsyncApi {
84    async fn get_progress();
85    async fn get_related_entities();
86    async fn get_data();
87    async fn set_data();
88}
89
90#[cfg(test)]
91mod tests {
92    use super::*;
93    use crate::MockMosaikApi;
94    use mockall::predicate::*;
95    use serde_json::json;
96
97    #[test]
98    fn test_extra_method_with_logic() {
99        let mut mock = MockMosaikApi::new();
100        let args = vec![json!(1), json!("hello")];
101        let kwargs = Map::new();
102
103        // Implement the logic for extra_method
104        let actual_method = |method: &str| match method {
105            "example_method" => Ok(Value::String("example result".to_string())),
106            _ => Err(format!("Method not found: {method}")),
107        };
108
109        // Set up expectation
110        mock.expect_extra_method()
111            .with(
112                mockall::predicate::eq("example_method"),
113                mockall::predicate::eq(args.clone()),
114                mockall::predicate::eq(kwargs.clone()),
115            )
116            .returning(move |method, _, _| actual_method(method));
117
118        mock.expect_extra_method()
119            .with(
120                mockall::predicate::eq("unknown_method"),
121                mockall::predicate::eq(args.clone()),
122                mockall::predicate::eq(kwargs.clone()),
123            )
124            .returning(move |method, _, _| actual_method(method));
125
126        // Call the method and assert for known method
127        let result = mock.extra_method("example_method", &args, &kwargs);
128        assert_eq!(result, Ok(Value::String("example result".to_string())));
129
130        // Call the method and assert for unknown method
131        let result = mock.extra_method("unknown_method", &args, &kwargs);
132        assert_eq!(result, Err("Method not found: unknown_method".to_string()));
133    }
134}