fmi_sim/sim/fmi2/
io.rs

1//! FMI2-specific input and output implementation
2
3use arrow::{
4    array::{
5        ArrayRef, AsArray, BooleanBuilder, Float64Array, Float64Builder, Int32Builder,
6        StringBuilder, downcast_array,
7    },
8    datatypes::{DataType, Float64Type, Int32Type},
9};
10use fmi::{fmi2::instance::Common, traits::FmiInstance};
11use itertools::Itertools;
12
13use crate::sim::{
14    RecorderState,
15    interpolation::{Interpolate, PreLookup},
16    io::Recorder,
17    traits::{InstRecordValues, InstSetValues},
18};
19
20macro_rules! impl_recorder {
21    ($getter:ident, $builder_type:ident, $inst:expr, $vr:ident, $builder:ident) => {{
22        let mut value = [std::default::Default::default()];
23        $inst.$getter(&[*$vr], &mut value)?;
24        $builder
25            .as_any_mut()
26            .downcast_mut::<$builder_type>()
27            .expect(concat!("column is not ", stringify!($builder_type)))
28            .append_value(value[0]);
29    }};
30}
31
32macro_rules! impl_record_values {
33    ($inst:ty) => {
34        impl InstRecordValues for $inst {
35            fn record_outputs(
36                &mut self,
37                time: f64,
38                recorder: &mut RecorderState<Self>,
39            ) -> anyhow::Result<()> {
40                log::trace!("Recording variables at time {}", time);
41
42                recorder.time.append_value(time);
43                for Recorder {
44                    field,
45                    value_reference: vr,
46                    builder,
47                } in &mut recorder.recorders
48                {
49                    match field.data_type() {
50                        DataType::Boolean => {
51                            let mut value = [std::default::Default::default()];
52                            self.get_boolean(&[*vr], &mut value)?;
53                            builder
54                                .as_any_mut()
55                                .downcast_mut::<BooleanBuilder>()
56                                .expect(concat!("column is not ", stringify!($builder_type)))
57                                .append_value(value[0] > 0);
58                        }
59                        DataType::Int32 => {
60                            impl_recorder!(get_integer, Int32Builder, self, vr, builder)
61                        }
62                        DataType::Float64 => {
63                            impl_recorder!(get_real, Float64Builder, self, vr, builder)
64                        }
65                        DataType::Utf8 => {
66                            let mut values = vec![std::ffi::CString::new("").unwrap()];
67                            if self.get_string(&[*vr], &mut values).is_ok() {
68                                let string_value = values[0].to_str().unwrap_or("");
69                                builder
70                                    .as_any_mut()
71                                    .downcast_mut::<StringBuilder>()
72                                    .expect("column is not StringBuilder")
73                                    .append_value(string_value);
74                            } else {
75                                // Handle error case by appending empty string
76                                builder
77                                    .as_any_mut()
78                                    .downcast_mut::<StringBuilder>()
79                                    .expect("column is not StringBuilder")
80                                    .append_value("");
81                            }
82                        }
83                        _ => unimplemented!("Unsupported data type: {:?}", field.data_type()),
84                    }
85                }
86                Ok(())
87            }
88        }
89    };
90}
91
92macro_rules! impl_set_values {
93    ($t:ty) => {
94        impl InstSetValues for $t {
95            fn set_array(&mut self, vrs: &[Self::ValueRef], values: &ArrayRef) {
96                match values.data_type() {
97                    DataType::Boolean => {
98                        let values = values
99                            .as_boolean()
100                            .iter()
101                            .map(|x| x.unwrap() as i32)
102                            .collect_vec();
103                        let _ = self.set_boolean(vrs, &values);
104                    }
105                    DataType::Int32 => {
106                        let _ = self.set_integer(vrs, values.as_primitive::<Int32Type>().values());
107                    }
108                    DataType::Float64 => {
109                        let _ = self.set_real(vrs, values.as_primitive::<Float64Type>().values());
110                    }
111                    DataType::Utf8 => {
112                        let cstrings: Vec<std::ffi::CString> = values
113                            .as_string::<i32>()
114                            .iter()
115                            .flatten()
116                            .map(|s| std::ffi::CString::new(s).unwrap())
117                            .collect();
118                        let _ = self.set_string(vrs, &cstrings);
119                    }
120                    _ => unimplemented!("Unsupported data type"),
121                }
122            }
123
124            fn set_interpolated<I: Interpolate>(
125                &mut self,
126                vr: <Self as FmiInstance>::ValueRef,
127                pl: &PreLookup,
128                array: &ArrayRef,
129            ) -> anyhow::Result<()> {
130                match array.data_type() {
131                    DataType::Boolean => todo!(),
132                    DataType::Int32 => {
133                        let array = array.as_primitive::<Int32Type>();
134                        let value = I::interpolate(pl, &array);
135                        self.set_integer(&[vr], &[value])?;
136                    }
137                    DataType::Float64 => {
138                        let array: Float64Array = downcast_array(&array);
139                        let value = I::interpolate(pl, &array);
140                        self.set_real(&[vr], &[value])?;
141                    }
142                    _ => unimplemented!("Unsupported data type: {:?}", array.data_type()),
143                }
144                Ok(())
145            }
146        }
147    };
148}
149
150#[cfg(feature = "cs")]
151impl_set_values!(fmi::fmi2::instance::InstanceCS<'_>);
152#[cfg(feature = "cs")]
153impl_record_values!(fmi::fmi2::instance::InstanceCS<'_>);
154
155#[cfg(feature = "me")]
156impl_set_values!(fmi::fmi2::instance::InstanceME<'_>);
157#[cfg(feature = "me")]
158impl_record_values!(fmi::fmi2::instance::InstanceME<'_>);