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 ..
48 } in &mut recorder.recorders
49 {
50 log::trace!(
51 "Recording variable VR={} of type {:?}",
52 vr,
53 field.data_type()
54 );
55 match field.data_type() {
56 DataType::Boolean => {
57 let mut value = [std::default::Default::default()];
58 self.get_boolean(&[*vr], &mut value)?;
59 builder
60 .as_any_mut()
61 .downcast_mut::<BooleanBuilder>()
62 .expect(concat!("column is not ", stringify!($builder_type)))
63 .append_value(value[0] > 0);
64 }
65 DataType::Int32 => {
66 impl_recorder!(get_integer, Int32Builder, self, vr, builder)
67 }
68 DataType::Float64 => {
69 impl_recorder!(get_real, Float64Builder, self, vr, builder)
70 }
71 DataType::Utf8 => {
72 let mut values = vec![std::ffi::CString::new("").unwrap()];
73 if self.get_string(&[*vr], &mut values).is_ok() {
74 let string_value = values[0].to_str().unwrap_or("");
75 builder
76 .as_any_mut()
77 .downcast_mut::<StringBuilder>()
78 .expect("column is not StringBuilder")
79 .append_value(string_value);
80 } else {
81 builder
83 .as_any_mut()
84 .downcast_mut::<StringBuilder>()
85 .expect("column is not StringBuilder")
86 .append_value("");
87 }
88 }
89 _ => unimplemented!("Unsupported data type: {:?}", field.data_type()),
90 }
91 }
92
93 println!("Recorded outputs at time {}", time);
94 Ok(())
95 }
96 }
97 };
98}
99
100macro_rules! impl_set_values {
101 ($t:ty) => {
102 impl InstSetValues for $t {
103 fn set_array(&mut self, vrs: &[Self::ValueRef], values: &ArrayRef) {
104 match values.data_type() {
105 DataType::Boolean => {
106 let values = values
107 .as_boolean()
108 .iter()
109 .map(|x| x.unwrap() as i32)
110 .collect_vec();
111 let _ = self.set_boolean(vrs, &values);
112 }
113 DataType::Int32 => {
114 let _ = self.set_integer(vrs, values.as_primitive::<Int32Type>().values());
115 }
116 DataType::Float64 => {
117 let _ = self.set_real(vrs, values.as_primitive::<Float64Type>().values());
118 }
119 DataType::Utf8 => {
120 let cstrings: Vec<std::ffi::CString> = values
121 .as_string::<i32>()
122 .iter()
123 .flatten()
124 .map(|s| std::ffi::CString::new(s).unwrap())
125 .collect();
126 let _ = self.set_string(vrs, &cstrings);
127 }
128 _ => unimplemented!("Unsupported data type"),
129 }
130 }
131
132 fn set_interpolated<I: Interpolate>(
133 &mut self,
134 vr: <Self as FmiInstance>::ValueRef,
135 pl: &PreLookup,
136 array: &ArrayRef,
137 ) -> anyhow::Result<()> {
138 match array.data_type() {
139 DataType::Boolean => todo!(),
140 DataType::Int32 => {
141 let array = array.as_primitive::<Int32Type>();
142 let value = I::interpolate(pl, &array);
143 self.set_integer(&[vr], &[value])?;
144 }
145 DataType::Float64 => {
146 let array: Float64Array = downcast_array(&array);
147 let value = I::interpolate(pl, &array);
148 self.set_real(&[vr], &[value])?;
149 }
150 _ => unimplemented!("Unsupported data type: {:?}", array.data_type()),
151 }
152 Ok(())
153 }
154 }
155 };
156}
157
158#[cfg(feature = "cs")]
159impl_set_values!(fmi::fmi2::instance::InstanceCS);
160#[cfg(feature = "cs")]
161impl_record_values!(fmi::fmi2::instance::InstanceCS);
162
163#[cfg(feature = "me")]
164impl_set_values!(fmi::fmi2::instance::InstanceME);
165#[cfg(feature = "me")]
166impl_record_values!(fmi::fmi2::instance::InstanceME);