1use arrow::array::RecordBatch;
2use std::path::Path;
3
4use fmi::traits::{FmiEventHandler, FmiImport, FmiInstance};
5
6pub use io::{InputState, RecorderState};
7
8use crate::{Error, options};
9
10use self::{
11    interpolation::Linear,
12    params::SimParams,
13    traits::{FmiSim, InstRecordValues, InstSetValues, SimDefaultInitialize, SimHandleEvents},
14};
15
16#[cfg(feature = "fmi2")]
17pub mod fmi2;
18#[cfg(feature = "fmi3")]
19pub mod fmi3;
20mod interpolation;
21mod io;
22mod me;
23pub mod params;
24pub mod solver;
25pub mod traits;
26pub mod util;
27
28pub struct SimState<Inst>
29where
30    Inst: FmiInstance,
31{
32    sim_params: SimParams,
33    input_state: InputState<Inst>,
34    recorder_state: RecorderState<Inst>,
35    inst: Inst,
36    next_event_time: Option<f64>,
37}
38
39pub trait SimStateTrait<'a, Inst: FmiInstance, Import: FmiImport> {
40    fn new(
41        import: &'a Import,
42        sim_params: SimParams,
43        input_state: InputState<Inst>,
44        output_state: RecorderState<Inst>,
45    ) -> Result<Self, Error>
46    where
47        Self: Sized;
48}
49
50impl<Inst> SimHandleEvents for SimState<Inst>
51where
52    Inst: FmiEventHandler + InstSetValues + InstRecordValues,
53{
54    fn handle_events(
55        &mut self,
56        time: f64,
57        input_event: bool,
58        terminate_simulation: &mut bool,
59    ) -> Result<bool, Error> {
60        self.inst.record_outputs(time, &mut self.recorder_state)?;
61        self.inst.enter_event_mode().map_err(Into::into)?;
62        if input_event {
63            self.input_state
64                .apply_input::<Linear>(time, &mut self.inst, true, true, true)?;
65        }
66        let mut reset_solver = false;
67        let mut discrete_states_need_update = true;
68        let mut nominals_of_continuous_states_changed = false;
69        let mut values_of_continuous_states_changed = false;
70        while discrete_states_need_update {
71            self.inst
72                .update_discrete_states(
73                    &mut discrete_states_need_update,
74                    terminate_simulation,
75                    &mut nominals_of_continuous_states_changed,
76                    &mut values_of_continuous_states_changed,
77                    &mut self.next_event_time,
78                )
79                .map_err(Into::into)?;
80            if *terminate_simulation {
81                break;
82            }
83            reset_solver |=
84                nominals_of_continuous_states_changed || values_of_continuous_states_changed;
85        }
86        Ok(reset_solver)
87    }
88}
89
90#[derive(Default, Debug)]
91pub struct SimStats {
92    pub end_time: f64,
94    pub num_steps: usize,
96    pub num_events: usize,
98}
99
100pub fn simulate_with<Imp: FmiSim>(
102    input_data: Option<RecordBatch>,
103    interface: &options::Interface,
104    import: Imp,
105) -> Result<(RecordBatch, SimStats), Error> {
106    match interface {
107        #[cfg(feature = "me")]
108        options::Interface::ModelExchange(options) => import.simulate_me(options, input_data),
109        #[cfg(feature = "cs")]
110        options::Interface::CoSimulation(options) => import.simulate_cs(options, input_data),
111        #[cfg(feature = "se")]
112        options::Interface::ScheduledExecution(options) => unimplemented!(),
113        #[cfg(any(not(feature = "me"), not(feature = "cs")))]
114        _ => Err(fmi::Error::UnsupportedInterface(format!("{}", interface)).into()),
115    }
116}
117
118macro_rules! impl_sim_default_initialize {
119    ($inst:ty) => {
120        impl SimDefaultInitialize for SimState<$inst> {
121            fn default_initialize(&mut self) -> Result<(), Error> {
122                self.inst
123                    .enter_initialization_mode(
124                        self.sim_params.tolerance,
125                        self.sim_params.start_time,
126                        Some(self.sim_params.stop_time),
127                    )
128                    .map_err(fmi::Error::from)?;
129
130                self.inst
131                    .exit_initialization_mode()
132                    .map_err(fmi::Error::from)?;
133
134                if self.sim_params.event_mode_used {
135                    let mut discrete_states_need_update = true;
137                    let mut nominals_of_continuous_states_changed = false;
138                    let mut values_of_continuous_states_changed = false;
139                    while discrete_states_need_update {
140                        let mut terminate_simulation = false;
141
142                        self.inst
143                            .update_discrete_states(
144                                &mut discrete_states_need_update,
145                                &mut terminate_simulation,
146                                &mut nominals_of_continuous_states_changed,
147                                &mut values_of_continuous_states_changed,
148                                &mut self.next_event_time,
149                            )
150                            .map_err(fmi::Error::from)?;
151
152                        if terminate_simulation {
153                            self.inst.terminate().map_err(fmi::Error::from)?;
154                            log::error!("update_discrete_states() requested termination.");
155                            break;
156                        }
157                    }
158                }
159                Ok(())
160            }
161        }
162    };
163}
164
165#[cfg(feature = "me")]
166impl_sim_default_initialize!(fmi::fmi2::instance::InstanceME<'_>);
167#[cfg(feature = "cs")]
168impl SimDefaultInitialize for SimState<fmi::fmi2::instance::InstanceCS<'_>> {
169    fn default_initialize(&mut self) -> Result<(), Error> {
170        self.inst
171            .enter_initialization_mode(
172                self.sim_params.tolerance,
173                self.sim_params.start_time,
174                Some(self.sim_params.stop_time),
175            )
176            .map_err(fmi::Error::from)?;
177        self.inst
178            .exit_initialization_mode()
179            .map_err(fmi::Error::from)?;
180
181        Ok(())
182    }
183}
184
185#[cfg(feature = "me")]
186impl_sim_default_initialize!(fmi::fmi3::instance::InstanceME<'_>);
187#[cfg(feature = "cs")]
188impl_sim_default_initialize!(fmi::fmi3::instance::InstanceCS<'_>);
189
190macro_rules! impl_sim_initialize {
191    ($inst:ty) => {
192        impl traits::SimInitialize<$inst> for SimState<$inst> {
193            fn initialize<P: AsRef<Path>>(
194                &mut self,
195                start_values: io::StartValues<<$inst as FmiInstance>::ValueRef>,
196                initial_fmu_state_file: Option<P>,
197            ) -> Result<(), Error> {
198                if let Some(_initial_state_file) = &initial_fmu_state_file {
199                    unimplemented!("initial_fmu_state_file");
200                    }
202
203                traits::SimApplyStartValues::apply_start_values(self, &start_values)?;
205
206                self.input_state.apply_input::<interpolation::Linear>(
207                    self.sim_params.start_time,
208                    &mut self.inst,
209                    true,
210                    true,
211                    false,
212                )?;
213
214                if initial_fmu_state_file.is_none() {
216                    self.default_initialize()?;
217                }
218
219                Ok(())
220            }
221        }
222    };
223}
224
225#[cfg(feature = "me")]
226impl_sim_initialize!(fmi::fmi2::instance::InstanceME<'_>);
227#[cfg(feature = "me")]
228impl_sim_initialize!(fmi::fmi3::instance::InstanceME<'_>);
229#[cfg(feature = "cs")]
230impl_sim_initialize!(fmi::fmi2::instance::InstanceCS<'_>);
231#[cfg(feature = "cs")]
232impl_sim_initialize!(fmi::fmi3::instance::InstanceCS<'_>);