Skip to main content

fmi_export/fmi3/instance/
common.rs

1use super::ModelInstance;
2use crate::fmi3::{
3    Model, ModelState, UserModel,
4    traits::{Context, ModelGetSet, ModelLoggingCategory},
5};
6use fmi::{
7    EventFlags, InterfaceType,
8    fmi3::{Common, Fmi3Error, Fmi3Res, binding},
9};
10
11impl<M, C> Common for ModelInstance<M, C>
12where
13    M: Model + UserModel + ModelGetSet<M>,
14    C: Context<M>,
15{
16    fn get_version(&self) -> &str {
17        // Safety: binding::fmi3Version is a null-terminated byte array representing the version string
18        unsafe { str::from_utf8_unchecked(binding::fmi3Version) }
19    }
20
21    fn set_debug_logging(
22        &mut self,
23        logging_on: bool,
24        categories: &[&str],
25    ) -> Result<Fmi3Res, Fmi3Error> {
26        for &cat in categories.iter() {
27            if let Some(cat) = cat.parse::<M::LoggingCategory>().ok() {
28                self.context.set_logging(cat, logging_on);
29            } else {
30                self.context.log(
31                    Fmi3Error::Error.into(),
32                    M::LoggingCategory::default(),
33                    format_args!("Unknown logging category {cat}"),
34                );
35                return Err(Fmi3Error::Error);
36            }
37        }
38        Ok(Fmi3Res::OK)
39    }
40
41    fn enter_initialization_mode(
42        &mut self,
43        tolerance: Option<f64>,
44        start_time: f64,
45        stop_time: Option<f64>,
46    ) -> Result<Fmi3Res, Fmi3Error> {
47        self.context.log(
48            Fmi3Res::OK.into(),
49            Default::default(),
50            format_args!(
51                "enter_initialization_mode(tolerance: {tolerance:?}, start_time: {start_time:?}, stop_time: {stop_time:?})",
52            ),
53        );
54
55        match self.state {
56            ModelState::Instantiated => {
57                self.context.initialize(start_time, stop_time);
58                self.state = ModelState::InitializationMode;
59                Ok(Fmi3Res::OK)
60            }
61            _ => {
62                self.context.log(
63                    Fmi3Error::Error.into(),
64                    M::LoggingCategory::default(),
65                    format_args!(
66                        "enter_initialization_mode() called in invalid state {:?}",
67                        self.state
68                    ),
69                );
70                Err(Fmi3Error::Error)
71            }
72        }
73    }
74
75    fn exit_initialization_mode(&mut self) -> Result<Fmi3Res, Fmi3Error> {
76        self.context.log(
77            Fmi3Res::OK.into(),
78            Default::default(),
79            format_args!("exit_initialization_mode()"),
80        );
81
82        // if values were set and no get call triggered update before, ensure calculated values are updated now
83        if self.is_dirty_values {
84            self.model.calculate_values(&self.context)?;
85            self.is_dirty_values = false;
86        }
87
88        match self.instance_type {
89            InterfaceType::ModelExchange => {
90                self.state = ModelState::EventMode;
91            }
92            InterfaceType::CoSimulation => {
93                //TODO support event mode switch
94                let event_mode_used = false;
95                if event_mode_used {
96                    self.state = ModelState::EventMode;
97                } else {
98                    self.state = ModelState::StepMode;
99                }
100            }
101            InterfaceType::ScheduledExecution => {
102                self.state = ModelState::ClockActivationMode;
103            }
104        }
105
106        self.model.configurate(&self.context)?;
107
108        Ok(Fmi3Res::OK)
109    }
110
111    fn terminate(&mut self) -> Result<Fmi3Res, Fmi3Error> {
112        self.context.log(
113            Fmi3Res::OK.into(),
114            Default::default(),
115            format_args!("terminate()"),
116        );
117        self.state = ModelState::Terminated;
118        Ok(Fmi3Res::OK)
119    }
120
121    fn reset(&mut self) -> Result<Fmi3Res, Fmi3Error> {
122        self.state = ModelState::Instantiated;
123        self.context.initialize(0.0, None);
124        self.model.set_start_values();
125        Ok(Fmi3Res::OK)
126    }
127
128    fn enter_configuration_mode(&mut self) -> Result<Fmi3Res, Fmi3Error> {
129        self.context.log(
130            Fmi3Res::OK.into(),
131            M::LoggingCategory::trace_category(),
132            format_args!("enter_configuration_mode()"),
133        );
134
135        match self.state {
136            ModelState::Instantiated => {
137                self.state = ModelState::ConfigurationMode;
138            }
139            _ => {
140                self.state = ModelState::ReconfigurationMode;
141            }
142        }
143
144        Ok(Fmi3Res::OK)
145    }
146
147    fn exit_configuration_mode(&mut self) -> Result<Fmi3Res, Fmi3Error> {
148        self.context.log(
149            Fmi3Res::OK.into(),
150            M::LoggingCategory::trace_category(),
151            format_args!("exit_configuration_mode()"),
152        );
153
154        match self.state {
155            ModelState::ConfigurationMode => {
156                self.state = ModelState::Instantiated;
157            }
158
159            ModelState::ReconfigurationMode => {
160                match self.instance_type {
161                    InterfaceType::ModelExchange => {
162                        self.state = ModelState::EventMode;
163                    }
164                    InterfaceType::CoSimulation => {
165                        //TODO support event mode switch
166                        let event_mode_used = false;
167                        if event_mode_used {
168                            self.state = ModelState::EventMode;
169                        } else {
170                            self.state = ModelState::StepMode;
171                        }
172                    }
173                    InterfaceType::ScheduledExecution => {
174                        self.state = ModelState::ClockActivationMode;
175                    }
176                }
177            }
178
179            _ => {
180                self.context.log(
181                    Fmi3Error::Error.into(),
182                    M::LoggingCategory::default(),
183                    format_args!(
184                        "exit_configuration_mode() called in invalid state {:?}",
185                        self.state
186                    ),
187                );
188                return Err(Fmi3Error::Error);
189            }
190        }
191
192        Ok(Fmi3Res::OK)
193    }
194
195    fn enter_event_mode(&mut self) -> Result<Fmi3Res, Fmi3Error> {
196        self.context.log(
197            Fmi3Res::OK.into(),
198            M::LoggingCategory::trace_category(),
199            format_args!("enter_event_mode()"),
200        );
201        self.state = ModelState::EventMode;
202        Ok(Fmi3Res::OK)
203    }
204
205    fn update_discrete_states(
206        &mut self,
207        event_flags: &mut EventFlags,
208    ) -> Result<Fmi3Res, Fmi3Error> {
209        self.context.log(
210            Fmi3Res::OK.into(),
211            M::LoggingCategory::trace_category(),
212            format_args!("update_discrete_states()"),
213        );
214        //UserModel::event_update(&mut self.model, &self.context, event_flags)
215        self.model.event_update(&self.context, event_flags)
216    }
217
218    fn get_number_of_variable_dependencies(
219        &mut self,
220        _vr: binding::fmi3ValueReference,
221    ) -> Result<usize, Fmi3Error> {
222        // Default implementation: no dependencies
223        Ok(0)
224    }
225
226    fn get_variable_dependencies(
227        &mut self,
228        _dependent: binding::fmi3ValueReference,
229    ) -> Result<Vec<fmi::fmi3::VariableDependency>, Fmi3Error> {
230        // Default implementation: no dependencies
231        Ok(Vec::new())
232    }
233}