Skip to main content

fmi_export/fmi3/traits/
model_get_set.rs

1use fmi::fmi3::{Fmi3Error, binding};
2
3use crate::fmi3::{Clock, types::Binary};
4
5use super::{Context, Model};
6
7/// Macro to generate getter and setter method declarations for the ModelGetSet trait
8macro_rules! model_getter_setter {
9    ($name:ident, $ty:ty) => {
10        paste::paste! {
11            /// Get [<$name>] values from the model
12            /// Returns the number of elements that were actually read
13            fn [<get_ $name>](
14                &self,
15                _vr: binding::fmi3ValueReference,
16                _values: &mut [$ty],
17                _context: &dyn Context<M>,
18            ) -> Result<usize, Fmi3Error> {
19                Err(Fmi3Error::Error)
20            }
21
22            /// Set [<$name>] values in the model
23            /// Returns the number of elements that were actually written
24            fn [<set_ $name>](
25                &mut self,
26                _vr: binding::fmi3ValueReference,
27                _values: &[$ty],
28                _context: &dyn Context<M>,
29            ) -> Result<usize, Fmi3Error> {
30                Err(Fmi3Error::Error)
31            }
32        }
33    };
34}
35
36/// Macro to implement ModelGetSet for primitive types
37macro_rules! impl_model_get_set_primitive {
38    ($name:ident, $ty:ty, $data_type:expr) => {
39        paste::paste! {
40            impl<M: Model> ModelGetSet<M> for $ty {
41                const FIELD_COUNT: usize = 1;
42                fn [<get_ $name>](
43                    &self,
44                    vr: binding::fmi3ValueReference,
45                    values: &mut [$ty],
46                    _context: &dyn Context<M>,
47                ) -> Result<usize, Fmi3Error> {
48                    if vr == 0 && !values.is_empty() {
49                        values[0] = *self;
50                        Ok(1)
51                    } else {
52                        Err(Fmi3Error::Error)
53                    }
54                }
55                fn [<set_ $name>](
56                    &mut self,
57                    vr: binding::fmi3ValueReference,
58                    values: &[$ty],
59                    _context: &dyn Context<M>,
60                ) -> Result<usize, Fmi3Error> {
61                    if vr == 0 && !values.is_empty() {
62                        *self = values[0];
63                        Ok(1)
64                    } else {
65                        Err(Fmi3Error::Error)
66                    }
67                }
68            }
69
70            impl<M: Model, const N: usize> ModelGetSet<M> for [$ty; N] {
71                const FIELD_COUNT: usize = N;
72                fn [<get_ $name>](
73                    &self,
74                    vr: binding::fmi3ValueReference,
75                    values: &mut [$ty],
76                    _context: &dyn Context<M>,
77                ) -> Result<usize, Fmi3Error> {
78                    if (vr as usize) < N && !values.is_empty() {
79                        let len = std::cmp::min(N - (vr as usize), values.len());
80                        values[..len].copy_from_slice(&self[(vr as usize)..(vr as usize + len)]);
81                        Ok(len)
82                    } else {
83                        Err(Fmi3Error::Error)
84                    }
85                }
86                fn [<set_ $name>](
87                    &mut self,
88                    vr: binding::fmi3ValueReference,
89                    values: &[$ty],
90                    _context: &dyn Context<M>,
91                ) -> Result<usize, Fmi3Error> {
92                    if (vr as usize) < N && !values.is_empty() {
93                        let len = std::cmp::min(N - (vr as usize), values.len());
94                        self[(vr as usize)..(vr as usize + len)].copy_from_slice(&values[..len]);
95                        Ok(len)
96                    } else {
97                        Err(Fmi3Error::Error)
98                    }
99                }
100            }
101        }
102    };
103}
104
105pub trait ModelGetSet<M: Model> {
106    /// The total number of primitive fields when flattened
107    const FIELD_COUNT: usize;
108
109    model_getter_setter!(boolean, bool);
110    model_getter_setter!(float32, f32);
111    model_getter_setter!(float64, f64);
112    model_getter_setter!(int8, i8);
113    model_getter_setter!(int16, i16);
114    model_getter_setter!(int32, i32);
115    model_getter_setter!(int64, i64);
116    model_getter_setter!(uint8, u8);
117    model_getter_setter!(uint16, u16);
118    model_getter_setter!(uint32, u32);
119    model_getter_setter!(uint64, u64);
120    model_getter_setter!(string, std::ffi::CString);
121
122    /// Get binary values from the model
123    /// Returns the sizes of the binary data that were actually read
124    fn get_binary(
125        &self,
126        _vr: binding::fmi3ValueReference,
127        _values: &mut [&mut [u8]],
128        _context: &dyn Context<M>,
129    ) -> Result<Vec<usize>, Fmi3Error> {
130        Err(Fmi3Error::Error)
131    }
132
133    /// Set binary values in the model
134    /// Returns the number of binary elements that were actually written
135    fn set_binary(
136        &mut self,
137        _vr: binding::fmi3ValueReference,
138        _values: &[&[u8]],
139        _context: &dyn Context<M>,
140    ) -> Result<usize, Fmi3Error> {
141        Err(Fmi3Error::Error)
142    }
143
144    /// Get clock values from the model
145    /// Note: For Output clocks, this method should reset the clock to false after reading
146    fn get_clock(
147        &mut self,
148        _vr: binding::fmi3ValueReference,
149        _value: &mut binding::fmi3Clock,
150        _context: &dyn Context<M>,
151    ) -> Result<(), Fmi3Error> {
152        Err(Fmi3Error::Error)
153    }
154
155    /// Set clock values in the model
156    fn set_clock(
157        &mut self,
158        _vr: binding::fmi3ValueReference,
159        _value: &binding::fmi3Clock,
160        _context: &dyn Context<M>,
161    ) -> Result<(), Fmi3Error> {
162        Err(Fmi3Error::Error)
163    }
164}
165
166impl_model_get_set_primitive!(boolean, bool, schema::DataType::Boolean);
167impl_model_get_set_primitive!(float32, f32, schema::DataType::Float32);
168impl_model_get_set_primitive!(float64, f64, schema::DataType::Float64);
169impl_model_get_set_primitive!(int8, i8, schema::DataType::Int8);
170impl_model_get_set_primitive!(int16, i16, schema::DataType::Int16);
171impl_model_get_set_primitive!(int32, i32, schema::DataType::Int32);
172impl_model_get_set_primitive!(int64, i64, schema::DataType::Int64);
173impl_model_get_set_primitive!(uint8, u8, schema::DataType::Uint8);
174impl_model_get_set_primitive!(uint16, u16, schema::DataType::Uint16);
175impl_model_get_set_primitive!(uint32, u32, schema::DataType::Uint32);
176impl_model_get_set_primitive!(uint64, u64, schema::DataType::Uint64);
177
178impl<M: Model> ModelGetSet<M> for String {
179    const FIELD_COUNT: usize = 1;
180    fn get_string(
181        &self,
182        vr: binding::fmi3ValueReference,
183        values: &mut [std::ffi::CString],
184        _context: &dyn Context<M>,
185    ) -> Result<usize, Fmi3Error> {
186        if vr == 0 && !values.is_empty() {
187            values[0] = std::ffi::CString::new(self.as_str()).unwrap();
188            Ok(1)
189        } else {
190            Err(Fmi3Error::Error)
191        }
192    }
193    fn set_string(
194        &mut self,
195        vr: binding::fmi3ValueReference,
196        values: &[std::ffi::CString],
197        _context: &dyn Context<M>,
198    ) -> Result<usize, Fmi3Error> {
199        if vr == 0 && !values.is_empty() {
200            *self = values[0]
201                .to_str()
202                .map_err(|_| Fmi3Error::Error)?
203                .to_string();
204            Ok(1)
205        } else {
206            Err(Fmi3Error::Error)
207        }
208    }
209}
210
211impl<M: Model> ModelGetSet<M> for Clock {
212    const FIELD_COUNT: usize = 1;
213    fn get_clock(
214        &mut self,
215        vr: binding::fmi3ValueReference,
216        value: &mut binding::fmi3Clock,
217        _context: &dyn Context<M>,
218    ) -> Result<(), Fmi3Error> {
219        if vr == 0 {
220            *value = self.0;
221            // Reset clock since GetClock may only return true once per activation
222            self.0 = false;
223            Ok(())
224        } else {
225            Err(Fmi3Error::Error)
226        }
227    }
228    fn set_clock(
229        &mut self,
230        vr: binding::fmi3ValueReference,
231        value: &binding::fmi3Clock,
232        _context: &dyn Context<M>,
233    ) -> Result<(), Fmi3Error> {
234        if vr == 0 {
235            self.0 = *value;
236            Ok(())
237        } else {
238            Err(Fmi3Error::Error)
239        }
240    }
241}
242
243impl<M: Model> ModelGetSet<M> for Binary {
244    const FIELD_COUNT: usize = 1;
245    fn get_binary(
246        &self,
247        vr: binding::fmi3ValueReference,
248        values: &mut [&mut [u8]],
249        _context: &dyn Context<M>,
250    ) -> Result<Vec<usize>, Fmi3Error> {
251        if vr == 0 && !values.is_empty() {
252            let len = std::cmp::min(self.0.len(), values[0].len());
253            values[0][..len].copy_from_slice(&self.0[..len]);
254            Ok(vec![len])
255        } else {
256            Err(Fmi3Error::Error)
257        }
258    }
259    fn set_binary(
260        &mut self,
261        vr: binding::fmi3ValueReference,
262        values: &[&[u8]],
263        _context: &dyn Context<M>,
264    ) -> Result<usize, Fmi3Error> {
265        if vr == 0 && !values.is_empty() {
266            self.0.clear();
267            self.0.extend_from_slice(values[0]);
268            Ok(1)
269        } else {
270            Err(Fmi3Error::Error)
271        }
272    }
273}
274
275pub trait ModelGetSetStates {
276    /// The number of continuous states in the model
277    const NUM_STATES: usize;
278
279    /// Get continuous states from the model
280    /// Returns the current values of all continuous state variables
281    fn get_continuous_states(&self, states: &mut [f64]) -> Result<(), Fmi3Error>;
282
283    /// Set continuous states in the model
284    /// Sets new values for all continuous state variables
285    fn set_continuous_states(&mut self, states: &[f64]) -> Result<(), Fmi3Error>;
286
287    /// Get derivatives of continuous states
288    /// Returns the first-order time derivatives of all continuous state variables
289    fn get_continuous_state_derivatives(
290        &mut self,
291        derivatives: &mut [f64],
292    ) -> Result<(), Fmi3Error>;
293}
294
295impl ModelGetSetStates for f64 {
296    const NUM_STATES: usize = 1;
297
298    fn get_continuous_states(&self, states: &mut [f64]) -> Result<(), Fmi3Error> {
299        if states.is_empty() {
300            Err(Fmi3Error::Error)
301        } else {
302            states[0] = *self;
303            Ok(())
304        }
305    }
306
307    fn set_continuous_states(&mut self, states: &[f64]) -> Result<(), Fmi3Error> {
308        if states.is_empty() {
309            Err(Fmi3Error::Error)
310        } else {
311            *self = states[0];
312            Ok(())
313        }
314    }
315
316    fn get_continuous_state_derivatives(
317        &mut self,
318        derivatives: &mut [f64],
319    ) -> Result<(), Fmi3Error> {
320        if derivatives.is_empty() {
321            Err(Fmi3Error::Error)
322        } else {
323            derivatives[0] = *self;
324            Ok(())
325        }
326    }
327}
328
329impl<const N: usize> ModelGetSetStates for [f64; N] {
330    const NUM_STATES: usize = N;
331
332    fn get_continuous_states(&self, states: &mut [f64]) -> Result<(), Fmi3Error> {
333        if states.len() < Self::NUM_STATES {
334            Err(Fmi3Error::Error)
335        } else {
336            states[0..N].copy_from_slice(self);
337            Ok(())
338        }
339    }
340
341    fn set_continuous_states(&mut self, states: &[f64]) -> Result<(), Fmi3Error> {
342        if states.len() < Self::NUM_STATES {
343            Err(Fmi3Error::Error)
344        } else {
345            self.copy_from_slice(&states[0..N]);
346            Ok(())
347        }
348    }
349
350    fn get_continuous_state_derivatives(
351        &mut self,
352        derivatives: &mut [f64],
353    ) -> Result<(), Fmi3Error> {
354        if derivatives.len() < Self::NUM_STATES {
355            Err(Fmi3Error::Error)
356        } else {
357            derivatives[0..N].copy_from_slice(self);
358            Ok(())
359        }
360    }
361}