1use 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 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<'_>);