Skip to main content

fmi_export/fmi3/traits/
wrappers.rs

1//! Traits that implement safe wrappers around the C-typed APIs
2//!
3//! # Notes:
4//!
5//! 1. Exported C-ABI functions delegate directly to these trait functions.
6//!   - the entire API must invariably be available through these traits.
7//! 2. The instantiation functions:
8//!  - must fail here if the model doesn't "support" the requested interface
9#![allow(
10    clippy::missing_safety_doc,
11    clippy::too_many_arguments,
12    clippy::useless_conversion
13)]
14
15use ::std::ffi::CString;
16
17use crate::fmi3::{
18    ModelGetSetStates, ModelInstance, UserModel,
19    instance::{IntermediateUpdateClosure, LogMessageClosure, context::BasicContext},
20    traits::ModelGetSet,
21};
22
23use super::{Context, Model};
24
25use fmi::fmi3::CoSimulation;
26
27use ::fmi::fmi3::Fmi3Status;
28use fmi::fmi3::{Common, Fmi3Res, GetSet, ModelExchange, ScheduledExecution, binding};
29
30/// Safely dereferences an FMI instance pointer for Model Exchange instances.
31#[macro_export]
32macro_rules! checked_deref_me {
33    ($ptr:expr, $ty:ty) => {{
34        if ($ptr as *mut ::std::os::raw::c_void).is_null() {
35            eprintln!("Invalid FMU instance");
36            return ::fmi::fmi3::binding::fmi3Status_fmi3Error;
37        }
38        let instance = unsafe {
39            &mut *($ptr as *mut $crate::fmi3::ModelInstance<
40                $ty,
41                $crate::fmi3::instance::context::BasicContext<$ty>,
42            >)
43        };
44        instance
45    }};
46}
47
48/// Safely dereferences an FMI instance pointer for Co-Simulation instances with wrapper context.
49#[macro_export]
50macro_rules! checked_deref_cs {
51    ($ptr:expr, $ty:ty) => {{
52        if ($ptr as *mut ::std::os::raw::c_void).is_null() {
53            eprintln!("Invalid FMU instance");
54            return ::fmi::fmi3::binding::fmi3Status_fmi3Error;
55        }
56        let instance = unsafe {
57            &mut *($ptr as *mut $crate::fmi3::ModelInstance<
58                $ty,
59                $crate::fmi3::instance::context::BasicContext<$ty>,
60            >)
61        };
62        instance
63    }};
64}
65
66/// Dispatches a method call based on the runtime instance_type.
67/// This is used for Common trait methods that must work for any instance type (ME/CS/SE).
68#[macro_export]
69macro_rules! dispatch_by_instance_type {
70    ($ptr:expr, $ty:ty, $method:ident $(, $arg:expr)*) => {{
71        if ($ptr as *mut ::std::os::raw::c_void).is_null() {
72            eprintln!("Invalid FMU instance");
73            return ::fmi::fmi3::binding::fmi3Status_fmi3Error;
74        }
75
76        // Read instance_type field directly to determine which concrete type to use
77        // Safety: instance_type is the first field for all ModelInstance<M, C> types
78        let instance_type = unsafe {
79            let temp = $ptr as *const $crate::fmi3::ModelInstance<$ty, $crate::fmi3::instance::context::BasicContext<$ty>>;
80            // Read the first field directly instead of calling a method
81            (*temp).instance_type
82        };
83
84        match instance_type {
85            fmi::InterfaceType::ModelExchange => {
86                let instance = unsafe {
87                    &mut *($ptr as *mut $crate::fmi3::ModelInstance<$ty, $crate::fmi3::instance::context::BasicContext<$ty>>)
88                };
89                instance.$method($($arg),*)
90            }
91            fmi::InterfaceType::CoSimulation => {
92                let instance = unsafe {
93                    &mut *($ptr as *mut $crate::fmi3::ModelInstance<$ty, $crate::fmi3::instance::context::BasicContext<$ty>>)
94                };
95                instance.$method($($arg),*)
96            }
97            fmi::InterfaceType::ScheduledExecution => {
98                let instance = unsafe {
99                    &mut *($ptr as *mut $crate::fmi3::ModelInstance<$ty, $crate::fmi3::instance::context::BasicContext<$ty>>)
100                };
101                instance.$method($($arg),*)
102            }
103        }
104    }};
105}
106
107#[macro_export]
108macro_rules! wrapper_getset_functions {
109    ($type_name:ident, $fmi_type:ty, $get_method:ident, $set_method:ident) => {
110        $crate::paste::paste! {
111            unsafe extern "C" fn [<fmi3_get_ $type_name:snake>](
112                instance: binding::fmi3Instance,
113                value_references: *const binding::fmi3ValueReference,
114                n_value_references: usize,
115                values: *mut $fmi_type,
116                n_values: usize,
117            ) -> binding::fmi3Status {
118                // Require at least one output slot per value reference; array variables may need more.
119                if n_values < n_value_references {
120                    eprintln!("FMI3: Array length mismatch in fmi3Get{}: value_references={}, values={}",
121                             stringify!($type_name), n_value_references, n_values);
122                    return ::fmi::fmi3::binding::fmi3Status_fmi3Error;
123                }
124
125                let value_refs = unsafe { std::slice::from_raw_parts(value_references, n_value_references) };
126                let values = unsafe { std::slice::from_raw_parts_mut(values, n_values) };
127
128                match $crate::dispatch_by_instance_type!(instance, Self, $get_method, value_refs, values) {
129                    Ok(res) => {
130                        let status: ::fmi::fmi3::Fmi3Status = res.into();
131                        status.into()
132                    }
133                    Err(_) => binding::fmi3Status_fmi3Error,
134                }
135            }
136
137            unsafe extern "C" fn [<fmi3_set_ $type_name:snake>](
138                instance: binding::fmi3Instance,
139                value_references: *const binding::fmi3ValueReference,
140                n_value_references: usize,
141                values: *const $fmi_type,
142                n_values: usize,
143            ) -> binding::fmi3Status {
144                // Require at least one input per value reference; array variables may need more.
145                if n_values < n_value_references {
146                    eprintln!("FMI3: Array length mismatch in fmi3Set{}: value_references={}, values={}",
147                             stringify!($type_name), n_value_references, n_values);
148                    return binding::fmi3Status_fmi3Error;
149                }
150
151                let value_refs = unsafe { std::slice::from_raw_parts(value_references, n_value_references) };
152                let values = unsafe { std::slice::from_raw_parts(values, n_values) };
153
154                match $crate::dispatch_by_instance_type!(instance, Self, $set_method, value_refs, values) {
155                    Ok(res) => {
156                        let status: ::fmi::fmi3::Fmi3Status = res.into();
157                        status.into()
158                    }
159                    Err(_) => binding::fmi3Status_fmi3Error,
160                }
161            }
162        }
163    };
164}
165
166pub trait Fmi3Common: Model + UserModel + ModelGetSet<Self> + ModelGetSetStates + Sized
167where
168    Self: 'static,
169{
170    #[inline(always)]
171    unsafe fn fmi3_get_version() -> *const ::std::os::raw::c_char {
172        binding::fmi3Version.as_ptr() as *const _
173    }
174
175    #[inline(always)]
176    unsafe fn fmi3_set_debug_logging(
177        instance: binding::fmi3Instance,
178        logging_on: binding::fmi3Boolean,
179        n_categories: usize,
180        categories: *const binding::fmi3String,
181    ) -> binding::fmi3Status {
182        let categories = unsafe { std::slice::from_raw_parts(categories, n_categories) }
183            .iter()
184            .filter_map(|cat| unsafe { std::ffi::CStr::from_ptr(*cat) }.to_str().ok())
185            .collect::<::std::vec::Vec<_>>();
186        match dispatch_by_instance_type!(instance, Self, set_debug_logging, logging_on, &categories)
187        {
188            Ok(res) => {
189                let status: Fmi3Status = res.into();
190                status.into()
191            }
192            Err(_) => binding::fmi3Status_fmi3Error,
193        }
194    }
195
196    #[inline(always)]
197    unsafe extern "C" fn fmi3_instantiate_model_exchange(
198        instance_name: binding::fmi3String,
199        instantiation_token: binding::fmi3String,
200        resource_path: binding::fmi3String,
201        _visible: binding::fmi3Boolean,
202        logging_on: binding::fmi3Boolean,
203        _instance_environment: binding::fmi3InstanceEnvironment,
204        log_message: binding::fmi3LogMessageCallback,
205    ) -> binding::fmi3Instance {
206        let name = unsafe { ::std::ffi::CStr::from_ptr(instance_name) }
207            .to_string_lossy()
208            .into_owned();
209        let token = unsafe { ::std::ffi::CStr::from_ptr(instantiation_token) }.to_string_lossy();
210        let resource_path = ::std::path::PathBuf::from(
211            unsafe { ::std::ffi::CStr::from_ptr(resource_path) }
212                .to_string_lossy()
213                .into_owned(),
214        );
215
216        // Wrap the C callback in a Rust closure
217        let log_message: LogMessageClosure = if let Some(cb) = log_message {
218            Box::new(
219                move |status: Fmi3Status, category: &str, args: std::fmt::Arguments<'_>| {
220                    let category_c = CString::new(category).unwrap_or_default();
221                    let message_c = CString::new(args.to_string()).unwrap_or_default();
222                    unsafe {
223                        cb(
224                            std::ptr::null_mut() as binding::fmi3InstanceEnvironment,
225                            status.into(),
226                            category_c.as_ptr(),
227                            message_c.as_ptr(),
228                        )
229                    };
230                },
231            )
232        } else {
233            Box::new(
234                move |status: Fmi3Status, category: &str, args: std::fmt::Arguments<'_>| {
235                    let category_c = CString::new(category).unwrap_or_default();
236                    let message_c = CString::new(args.to_string()).unwrap_or_default();
237                    eprintln!(
238                        "Log (status: {:?}, category: {}): {}",
239                        status,
240                        category_c.to_string_lossy(),
241                        message_c.to_string_lossy()
242                    );
243                },
244            )
245        };
246
247        if !Self::SUPPORTS_MODEL_EXCHANGE {
248            eprintln!("Model Exchange not supported by this FMU");
249            return ::std::ptr::null_mut();
250        }
251
252        let context = BasicContext::new(logging_on, log_message, resource_path, false, None);
253
254        match crate::fmi3::ModelInstance::<Self, BasicContext<Self>>::new(
255            name,
256            &token,
257            context,
258            fmi::InterfaceType::ModelExchange,
259        ) {
260            Ok(instance) => ::std::boxed::Box::into_raw(::std::boxed::Box::new(instance))
261                as binding::fmi3Instance,
262            Err(_) => {
263                eprintln!("Failed to instantiate FMU: invalid instantiation token");
264                ::std::ptr::null_mut()
265            }
266        }
267    }
268
269    #[inline(always)]
270    unsafe extern "C" fn fmi3_instantiate_co_simulation(
271        instance_name: binding::fmi3String,
272        instantiation_token: binding::fmi3String,
273        resource_path: binding::fmi3String,
274        _visible: binding::fmi3Boolean,
275        _logging_on: binding::fmi3Boolean,
276        _event_mode_used: binding::fmi3Boolean,
277        _early_return_allowed: binding::fmi3Boolean,
278        _required_intermediate_variables: *const binding::fmi3ValueReference,
279        _n_required_intermediate_variables: usize,
280        _instance_environment: binding::fmi3InstanceEnvironment,
281        _log_message: binding::fmi3LogMessageCallback,
282        intermediate_update: binding::fmi3IntermediateUpdateCallback,
283    ) -> binding::fmi3Instance {
284        let name = unsafe { ::std::ffi::CStr::from_ptr(instance_name) }
285            .to_string_lossy()
286            .into_owned();
287        let token = unsafe { ::std::ffi::CStr::from_ptr(instantiation_token) }.to_string_lossy();
288        let resource_path = ::std::path::PathBuf::from(
289            unsafe { ::std::ffi::CStr::from_ptr(resource_path) }
290                .to_string_lossy()
291                .into_owned(),
292        );
293
294        let intermediate_update: Option<IntermediateUpdateClosure> =
295            intermediate_update.map(|cb| {
296                let closure: IntermediateUpdateClosure = Box::new(
297                    move |time: f64,
298                          variable_set_requested: bool,
299                          variable_get_allowed: bool,
300                          step_finished: bool,
301                          can_return_early: bool|
302                          -> Option<f64> {
303                        let mut early_return_requested: binding::fmi3Boolean = false.into();
304                        let mut early_return_time: binding::fmi3Float64 = 0.0;
305                        unsafe {
306                            cb(
307                                std::ptr::null_mut() as binding::fmi3InstanceEnvironment,
308                                time,
309                                variable_set_requested.into(),
310                                variable_get_allowed.into(),
311                                step_finished.into(),
312                                can_return_early.into(),
313                                &mut early_return_requested as *mut binding::fmi3Boolean,
314                                &mut early_return_time as *mut binding::fmi3Float64,
315                            )
316                        };
317
318                        if early_return_requested.into() {
319                            Some(early_return_time)
320                        } else {
321                            None
322                        }
323                    },
324                );
325                closure
326            });
327
328        // Check if Co-Simulation is supported
329        if !Self::SUPPORTS_CO_SIMULATION {
330            eprintln!("Co-Simulation not supported by this FMU");
331            return ::std::ptr::null_mut();
332        }
333
334        let logging_on = _logging_on.into();
335        let log_message: LogMessageClosure = if let Some(cb) = _log_message {
336            Box::new(
337                move |status: Fmi3Status, category: &str, args: std::fmt::Arguments<'_>| {
338                    let category_c = CString::new(category).unwrap_or_default();
339                    let message_c = CString::new(args.to_string()).unwrap_or_default();
340                    unsafe {
341                        cb(
342                            std::ptr::null_mut() as binding::fmi3InstanceEnvironment,
343                            status.into(),
344                            category_c.as_ptr(),
345                            message_c.as_ptr(),
346                        )
347                    };
348                },
349            )
350        } else {
351            Box::new(
352                move |status: Fmi3Status, category: &str, args: std::fmt::Arguments<'_>| {
353                    let category_c = CString::new(category).unwrap_or_default();
354                    let message_c = CString::new(args.to_string()).unwrap_or_default();
355                    eprintln!(
356                        "Log (status: {:?}, category: {}): {}",
357                        status,
358                        category_c.to_string_lossy(),
359                        message_c.to_string_lossy()
360                    );
361                },
362            )
363        };
364
365        let early_return_allowed = _early_return_allowed.into();
366        let context = BasicContext::new(
367            logging_on,
368            log_message,
369            resource_path,
370            early_return_allowed,
371            intermediate_update,
372        );
373
374        match crate::fmi3::ModelInstance::<Self, BasicContext<Self>>::new(
375            name,
376            &token,
377            context,
378            fmi::InterfaceType::CoSimulation,
379        ) {
380            Ok(instance) => ::std::boxed::Box::into_raw(::std::boxed::Box::new(instance))
381                as binding::fmi3Instance,
382            Err(_) => {
383                eprintln!("Failed to instantiate FMU: invalid instantiation token");
384                ::std::ptr::null_mut()
385            }
386        }
387    }
388
389    #[inline(always)]
390    unsafe fn fmi3_instantiate_scheduled_execution(
391        instance_name: binding::fmi3String,
392        instantiation_token: binding::fmi3String,
393        resource_path: binding::fmi3String,
394        _visible: binding::fmi3Boolean,
395        _logging_on: binding::fmi3Boolean,
396        _instance_environment: binding::fmi3InstanceEnvironment,
397        _log_message: binding::fmi3LogMessageCallback,
398        _clock_update: binding::fmi3ClockUpdateCallback,
399        _lock_preemption: binding::fmi3LockPreemptionCallback,
400        _unlock_preemption: binding::fmi3UnlockPreemptionCallback,
401    ) -> binding::fmi3Instance {
402        let _name = unsafe { ::std::ffi::CStr::from_ptr(instance_name) }
403            .to_string_lossy()
404            .into_owned();
405        let _token = unsafe { ::std::ffi::CStr::from_ptr(instantiation_token) }.to_string_lossy();
406        let _resource_path = ::std::path::PathBuf::from(
407            unsafe { ::std::ffi::CStr::from_ptr(resource_path) }
408                .to_string_lossy()
409                .into_owned(),
410        );
411
412        todo!("Scheduled-Execution not yet implemented");
413    }
414
415    #[inline(always)]
416    unsafe fn fmi3_free_instance(instance: binding::fmi3Instance) {
417        if instance.is_null() {
418            eprintln!("Invalid FMU instance");
419            return;
420        }
421
422        // Read instance_type to determine which concrete type to use
423        // Safety: instance_type is at the same offset for all ModelInstance<M, C> types
424        let instance_type = unsafe {
425            let temp = instance
426                as *const crate::fmi3::ModelInstance<
427                    Self,
428                    crate::fmi3::instance::context::BasicContext<Self>,
429                >;
430            (*temp).instance_type()
431        };
432
433        // Drop the correct concrete type based on instance_type
434        match instance_type {
435            fmi::InterfaceType::ModelExchange => {
436                let _this = unsafe {
437                    ::std::boxed::Box::from_raw(
438                        instance
439                            as *mut crate::fmi3::ModelInstance<
440                                Self,
441                                crate::fmi3::instance::context::BasicContext<Self>,
442                            >,
443                    )
444                };
445                _this.context().log(
446                    Fmi3Res::OK.into(),
447                    Default::default(),
448                    format_args!("{}: fmi3FreeInstance()", _this.instance_name()),
449                );
450                // _this dropped here
451            }
452            fmi::InterfaceType::CoSimulation => {
453                let _this = unsafe {
454                    ::std::boxed::Box::from_raw(
455                        instance
456                            as *mut crate::fmi3::ModelInstance<
457                                Self,
458                                crate::fmi3::instance::context::BasicContext<Self>,
459                            >,
460                    )
461                };
462                _this.context().log(
463                    Fmi3Res::OK.into(),
464                    Default::default(),
465                    format_args!("{}: fmi3FreeInstance()", _this.instance_name()),
466                );
467                // _this dropped here
468            }
469            fmi::InterfaceType::ScheduledExecution => {
470                // TODO: Add SEContext when implemented
471                eprintln!("Scheduled Execution not yet implemented");
472            }
473        }
474    }
475
476    #[inline(always)]
477    unsafe fn fmi3_enter_initialization_mode(
478        instance: binding::fmi3Instance,
479        tolerance_defined: binding::fmi3Boolean,
480        tolerance: binding::fmi3Float64,
481        start_time: binding::fmi3Float64,
482        stop_time_defined: binding::fmi3Boolean,
483        stop_time: binding::fmi3Float64,
484    ) -> binding::fmi3Status {
485        let tolerance = tolerance_defined.then_some(tolerance);
486        let stop_time = stop_time_defined.then_some(stop_time);
487        match dispatch_by_instance_type!(
488            instance,
489            Self,
490            enter_initialization_mode,
491            tolerance,
492            start_time,
493            stop_time
494        ) {
495            Ok(res) => {
496                let status: Fmi3Status = res.into();
497                status.into()
498            }
499            Err(_) => binding::fmi3Status_fmi3Error,
500        }
501    }
502
503    #[inline(always)]
504    unsafe fn fmi3_exit_initialization_mode(
505        instance: binding::fmi3Instance,
506    ) -> binding::fmi3Status {
507        match dispatch_by_instance_type!(instance, Self, exit_initialization_mode) {
508            Ok(res) => {
509                let status: Fmi3Status = res.into();
510                status.into()
511            }
512            Err(_) => binding::fmi3Status_fmi3Error,
513        }
514    }
515
516    #[inline(always)]
517    unsafe fn fmi3_enter_event_mode(instance: binding::fmi3Instance) -> binding::fmi3Status {
518        match dispatch_by_instance_type!(instance, Self, enter_event_mode) {
519            Ok(res) => {
520                let status: Fmi3Status = res.into();
521                status.into()
522            }
523            Err(_) => binding::fmi3Status_fmi3Error,
524        }
525    }
526
527    #[inline(always)]
528    unsafe fn fmi3_terminate(instance: binding::fmi3Instance) -> binding::fmi3Status {
529        match dispatch_by_instance_type!(instance, Self, terminate) {
530            Ok(res) => {
531                let status: Fmi3Status = res.into();
532                status.into()
533            }
534            Err(_) => binding::fmi3Status_fmi3Error,
535        }
536    }
537
538    #[inline(always)]
539    unsafe fn fmi3_reset(instance: binding::fmi3Instance) -> binding::fmi3Status {
540        match dispatch_by_instance_type!(instance, Self, reset) {
541            Ok(res) => {
542                let status: Fmi3Status = res.into();
543                status.into()
544            }
545            Err(_) => binding::fmi3Status_fmi3Error,
546        }
547    }
548
549    // FMU State functions
550    #[inline(always)]
551    unsafe fn fmi3_get_fmu_state(
552        _instance: binding::fmi3Instance,
553        _fmu_state: *mut binding::fmi3FMUState,
554    ) -> binding::fmi3Status {
555        todo!("FMU state not yet implemented");
556    }
557
558    #[inline(always)]
559    unsafe fn fmi3_set_fmu_state(
560        _instance: binding::fmi3Instance,
561        _fmu_state: binding::fmi3FMUState,
562    ) -> binding::fmi3Status {
563        todo!("FMU state not yet implemented");
564    }
565
566    #[inline(always)]
567    unsafe fn fmi3_free_fmu_state(
568        _instance: binding::fmi3Instance,
569        _fmu_state: *mut binding::fmi3FMUState,
570    ) -> binding::fmi3Status {
571        todo!("FMU state not yet implemented");
572    }
573
574    #[inline(always)]
575    unsafe fn fmi3_serialized_fmu_state_size(
576        _instance: binding::fmi3Instance,
577        _fmu_state: binding::fmi3FMUState,
578        _size: *mut usize,
579    ) -> binding::fmi3Status {
580        todo!("FMU state not yet implemented");
581    }
582
583    #[inline(always)]
584    unsafe fn fmi3_serialize_fmu_state(
585        _instance: binding::fmi3Instance,
586        _fmu_state: binding::fmi3FMUState,
587        _serialized_state: *mut binding::fmi3Byte,
588        _size: usize,
589    ) -> binding::fmi3Status {
590        todo!("FMU state not yet implemented");
591    }
592
593    #[inline(always)]
594    unsafe fn fmi3_deserialize_fmu_state(
595        _instance: binding::fmi3Instance,
596        _serialized_state: *const binding::fmi3Byte,
597        _size: usize,
598        _fmu_state: *mut binding::fmi3FMUState,
599    ) -> binding::fmi3Status {
600        todo!("FMU state not yet implemented");
601    }
602
603    // Derivative functions
604    #[inline(always)]
605    unsafe fn fmi3_get_directional_derivative(
606        _instance: binding::fmi3Instance,
607        _unknowns: *const binding::fmi3ValueReference,
608        _n_unknowns: usize,
609        _knowns: *const binding::fmi3ValueReference,
610        _n_knowns: usize,
611        _seed: *const binding::fmi3Float64,
612        _n_seed: usize,
613        _sensitivity: *mut binding::fmi3Float64,
614        _n_sensitivity: usize,
615    ) -> binding::fmi3Status {
616        //let _instance = checked_deref_me!(instance, Self);
617        todo!("Directional derivative not yet implemented");
618    }
619
620    #[inline(always)]
621    unsafe fn fmi3_get_adjoint_derivative(
622        _instance: binding::fmi3Instance,
623        _unknowns: *const binding::fmi3ValueReference,
624        _n_unknowns: usize,
625        _knowns: *const binding::fmi3ValueReference,
626        _n_knowns: usize,
627        _seed: *const binding::fmi3Float64,
628        _n_seed: usize,
629        _sensitivity: *mut binding::fmi3Float64,
630        _n_sensitivity: usize,
631    ) -> binding::fmi3Status {
632        //let _instance = checked_deref_me!(instance, Self);
633        todo!("Adjoint derivative not yet implemented");
634    }
635
636    // Configuration mode functions
637    #[inline(always)]
638    unsafe fn fmi3_enter_configuration_mode(
639        instance: binding::fmi3Instance,
640    ) -> binding::fmi3Status {
641        match dispatch_by_instance_type!(instance, Self, enter_configuration_mode) {
642            Ok(res) => {
643                let status: Fmi3Status = res.into();
644                status.into()
645            }
646            Err(_) => binding::fmi3Status_fmi3Error,
647        }
648    }
649
650    #[inline(always)]
651    unsafe fn fmi3_exit_configuration_mode(instance: binding::fmi3Instance) -> binding::fmi3Status {
652        match dispatch_by_instance_type!(instance, Self, exit_configuration_mode) {
653            Ok(res) => {
654                let status: Fmi3Status = res.into();
655                status.into()
656            }
657            Err(_) => binding::fmi3Status_fmi3Error,
658        }
659    }
660
661    // Clock related functions
662    #[inline(always)]
663    unsafe fn fmi3_get_interval_decimal(
664        _instance: binding::fmi3Instance,
665        _value_references: *const binding::fmi3ValueReference,
666        _n_value_references: usize,
667        _intervals: *mut binding::fmi3Float64,
668        _qualifiers: *mut binding::fmi3IntervalQualifier,
669    ) -> binding::fmi3Status {
670        //let _instance = checked_deref_me!(instance, Self);
671        todo!("Clock interval not yet implemented");
672    }
673
674    #[inline(always)]
675    unsafe fn fmi3_get_interval_fraction(
676        _instance: binding::fmi3Instance,
677        _value_references: *const binding::fmi3ValueReference,
678        _n_value_references: usize,
679        _counters: *mut binding::fmi3UInt64,
680        _resolutions: *mut binding::fmi3UInt64,
681        _qualifiers: *mut binding::fmi3IntervalQualifier,
682    ) -> binding::fmi3Status {
683        //let _instance = checked_deref_me!(instance, Self);
684        todo!("Clock interval not yet implemented");
685    }
686
687    #[inline(always)]
688    unsafe fn fmi3_get_shift_decimal(
689        _instance: binding::fmi3Instance,
690        _value_references: *const binding::fmi3ValueReference,
691        _n_value_references: usize,
692        _shifts: *mut binding::fmi3Float64,
693    ) -> binding::fmi3Status {
694        //let _instance = checked_deref_me!(instance, Self);
695        todo!("Clock interval not yet implemented");
696    }
697
698    #[inline(always)]
699    unsafe fn fmi3_get_shift_fraction(
700        _instance: binding::fmi3Instance,
701        _value_references: *const binding::fmi3ValueReference,
702        _n_value_references: usize,
703        _counters: *mut binding::fmi3UInt64,
704        _resolutions: *mut binding::fmi3UInt64,
705    ) -> binding::fmi3Status {
706        //let _instance = checked_deref_me!(instance, Self);
707        todo!("Clock interval not yet implemented");
708    }
709
710    #[inline(always)]
711    unsafe fn fmi3_set_interval_decimal(
712        _instance: binding::fmi3Instance,
713        _value_references: *const binding::fmi3ValueReference,
714        _n_value_references: usize,
715        _intervals: *const binding::fmi3Float64,
716    ) -> binding::fmi3Status {
717        //let _instance = checked_deref_me!(instance, Self);
718        todo!("Clock interval not yet implemented");
719    }
720
721    #[inline(always)]
722    unsafe fn fmi3_set_interval_fraction(
723        _instance: binding::fmi3Instance,
724        _value_references: *const binding::fmi3ValueReference,
725        _n_value_references: usize,
726        _counters: *const binding::fmi3UInt64,
727        _resolutions: *const binding::fmi3UInt64,
728    ) -> binding::fmi3Status {
729        //let _instance = checked_deref_me!(instance, Self);
730        todo!("Clock interval not yet implemented");
731    }
732
733    #[inline(always)]
734    unsafe fn fmi3_set_shift_decimal(
735        _instance: binding::fmi3Instance,
736        _value_references: *const binding::fmi3ValueReference,
737        _n_value_references: usize,
738        _shifts: *const binding::fmi3Float64,
739    ) -> binding::fmi3Status {
740        //let _instance = checked_deref_me!(instance, Self);
741        todo!("Clock interval not yet implemented");
742    }
743
744    #[inline(always)]
745    unsafe fn fmi3_set_shift_fraction(
746        _instance: binding::fmi3Instance,
747        _value_references: *const binding::fmi3ValueReference,
748        _n_value_references: usize,
749        _counters: *const binding::fmi3UInt64,
750        _resolutions: *const binding::fmi3UInt64,
751    ) -> binding::fmi3Status {
752        //let _instance = checked_deref_me!(instance, Self);
753        todo!("Clock interval not yet implemented");
754    }
755
756    #[inline(always)]
757    unsafe fn fmi3_evaluate_discrete_states(
758        _instance: binding::fmi3Instance,
759    ) -> binding::fmi3Status {
760        //let _instance = checked_deref_me!(instance, Self);
761        todo!("Discrete states not yet implemented");
762    }
763
764    #[inline(always)]
765    unsafe fn fmi3_update_discrete_states(
766        instance: binding::fmi3Instance,
767        discrete_states_need_update: *mut binding::fmi3Boolean,
768        terminate_simulation: *mut binding::fmi3Boolean,
769        nominals_of_continuous_states_changed: *mut binding::fmi3Boolean,
770        values_of_continuous_states_changed: *mut binding::fmi3Boolean,
771        next_event_time_defined: *mut binding::fmi3Boolean,
772        next_event_time: *mut binding::fmi3Float64,
773    ) -> binding::fmi3Status {
774        let mut event_flags = ::fmi::EventFlags::default();
775
776        // next_time_event is potentially used as an in-out parameter
777        if unsafe { *next_event_time_defined } {
778            event_flags.next_event_time = Some(unsafe { *next_event_time });
779        }
780
781        match dispatch_by_instance_type!(instance, Self, update_discrete_states, &mut event_flags) {
782            Ok(res) => {
783                unsafe {
784                    *discrete_states_need_update = event_flags.discrete_states_need_update;
785                    *terminate_simulation = event_flags.terminate_simulation;
786                    *nominals_of_continuous_states_changed =
787                        event_flags.nominals_of_continuous_states_changed;
788                    *values_of_continuous_states_changed =
789                        event_flags.values_of_continuous_states_changed;
790
791                    if let Some(event_time) = event_flags.next_event_time {
792                        *next_event_time_defined = true;
793                        *next_event_time = event_time;
794                    } else {
795                        *next_event_time_defined = false;
796                        *next_event_time = 0.0;
797                    }
798                }
799
800                let status: Fmi3Status = res.into();
801                status.into()
802            }
803            Err(_) => binding::fmi3Status_fmi3Error,
804        }
805    }
806
807    wrapper_getset_functions!(Float64, binding::fmi3Float64, get_float64, set_float64);
808    wrapper_getset_functions!(Float32, binding::fmi3Float32, get_float32, set_float32);
809    wrapper_getset_functions!(Int64, binding::fmi3Int64, get_int64, set_int64);
810    wrapper_getset_functions!(Int32, binding::fmi3Int32, get_int32, set_int32);
811    wrapper_getset_functions!(Int16, binding::fmi3Int16, get_int16, set_int16);
812    wrapper_getset_functions!(Int8, binding::fmi3Int8, get_int8, set_int8);
813    wrapper_getset_functions!(UInt64, binding::fmi3UInt64, get_uint64, set_uint64);
814    wrapper_getset_functions!(UInt32, binding::fmi3UInt32, get_uint32, set_uint32);
815    wrapper_getset_functions!(UInt16, binding::fmi3UInt16, get_uint16, set_uint16);
816    wrapper_getset_functions!(UInt8, binding::fmi3UInt8, get_uint8, set_uint8);
817    wrapper_getset_functions!(Boolean, binding::fmi3Boolean, get_boolean, set_boolean);
818
819    // String functions
820    #[inline(always)]
821    unsafe fn fmi3_get_string(
822        instance: binding::fmi3Instance,
823        value_references: *const binding::fmi3ValueReference,
824        n_value_references: usize,
825        values: *mut binding::fmi3String,
826        n_values: usize,
827    ) -> binding::fmi3Status {
828        if n_value_references != n_values {
829            eprintln!(
830                "FMI3: Array length mismatch in fmi3GetString: value_references={}, values={}",
831                n_value_references, n_values
832            );
833            return binding::fmi3Status_fmi3Error;
834        }
835
836        let value_refs =
837            unsafe { ::std::slice::from_raw_parts(value_references, n_value_references) };
838
839        // Create temporary buffer for CString results
840        let mut temp_strings = vec![::std::ffi::CString::default(); n_values];
841
842        match dispatch_by_instance_type!(instance, Self, get_string, value_refs, &mut temp_strings)
843        {
844            Ok(_) => {
845                // Copy C string pointers to output array
846                let values_slice = unsafe { ::std::slice::from_raw_parts_mut(values, n_values) };
847                for (i, cstring) in temp_strings.iter().enumerate() {
848                    values_slice[i] = cstring.as_ptr();
849                }
850                binding::fmi3Status_fmi3OK
851            }
852            Err(_) => binding::fmi3Status_fmi3Error,
853        }
854    }
855
856    #[inline(always)]
857    unsafe fn fmi3_set_string(
858        instance: binding::fmi3Instance,
859        value_references: *const binding::fmi3ValueReference,
860        n_value_references: usize,
861        values: *const binding::fmi3String,
862        n_values: usize,
863    ) -> binding::fmi3Status {
864        if n_value_references != n_values {
865            eprintln!(
866                "FMI3: Array length mismatch in fmi3SetString: value_references={}, values={}",
867                n_value_references, n_values
868            );
869            return binding::fmi3Status_fmi3Error;
870        }
871
872        let value_refs =
873            unsafe { ::std::slice::from_raw_parts(value_references, n_value_references) };
874        let string_ptrs = unsafe { ::std::slice::from_raw_parts(values, n_values) };
875
876        // Convert C strings to CString objects
877        let mut temp_strings = Vec::with_capacity(n_values);
878        for &ptr in string_ptrs {
879            if ptr.is_null() {
880                temp_strings.push(CString::default());
881            } else {
882                let cstring = unsafe { ::std::ffi::CStr::from_ptr(ptr) }.to_owned();
883                temp_strings.push(cstring);
884            }
885        }
886
887        match dispatch_by_instance_type!(instance, Self, set_string, value_refs, &temp_strings) {
888            Ok(_) => binding::fmi3Status_fmi3OK,
889            Err(_) => binding::fmi3Status_fmi3Error,
890        }
891    }
892
893    // Binary functions
894    #[inline(always)]
895    unsafe fn fmi3_get_binary(
896        instance: binding::fmi3Instance,
897        value_references: *const binding::fmi3ValueReference,
898        n_value_references: usize,
899        value_sizes: *mut usize,
900        values: *mut *mut binding::fmi3Byte,
901        n_values: usize,
902    ) -> binding::fmi3Status {
903        if n_value_references != n_values {
904            eprintln!(
905                "FMI3: Array length mismatch in fmi3GetBinary: value_references={}, values={}",
906                n_value_references, n_values
907            );
908            return binding::fmi3Status_fmi3Error;
909        }
910
911        let value_refs =
912            unsafe { ::std::slice::from_raw_parts(value_references, n_value_references) };
913        let sizes_slice = unsafe { ::std::slice::from_raw_parts_mut(value_sizes, n_values) };
914        let values_slice = unsafe { ::std::slice::from_raw_parts_mut(values, n_values) };
915
916        // Create temporary buffers for binary data
917        let mut temp_buffers: Vec<&mut [u8]> = Vec::with_capacity(n_values);
918        for i in 0..n_values {
919            if values_slice[i].is_null() || sizes_slice[i] == 0 {
920                temp_buffers.push(&mut []);
921            } else {
922                let buffer =
923                    unsafe { ::std::slice::from_raw_parts_mut(values_slice[i], sizes_slice[i]) };
924                temp_buffers.push(buffer);
925            }
926        }
927
928        match dispatch_by_instance_type!(instance, Self, get_binary, value_refs, &mut temp_buffers)
929        {
930            Ok(actual_sizes) => {
931                // Update the actual sizes
932                for (i, &size) in actual_sizes.iter().enumerate() {
933                    sizes_slice[i] = size;
934                }
935                binding::fmi3Status_fmi3OK
936            }
937            Err(_) => binding::fmi3Status_fmi3Error,
938        }
939    }
940
941    #[inline(always)]
942    unsafe fn fmi3_set_binary(
943        instance: binding::fmi3Instance,
944        value_references: *const binding::fmi3ValueReference,
945        n_value_references: usize,
946        value_sizes: *const usize,
947        values: *const *const binding::fmi3Byte,
948        n_values: usize,
949    ) -> binding::fmi3Status {
950        if n_value_references != n_values {
951            eprintln!(
952                "FMI3: Array length mismatch in fmi3SetBinary: value_references={}, values={}",
953                n_value_references, n_values
954            );
955            return binding::fmi3Status_fmi3Error;
956        }
957
958        let value_refs =
959            unsafe { ::std::slice::from_raw_parts(value_references, n_value_references) };
960        let sizes_slice = unsafe { ::std::slice::from_raw_parts(value_sizes, n_values) };
961        let values_slice = unsafe { ::std::slice::from_raw_parts(values, n_values) };
962
963        // Create temporary slices for binary data
964        let mut temp_buffers: Vec<&[u8]> = Vec::with_capacity(n_values);
965        for i in 0..n_values {
966            if values_slice[i].is_null() || sizes_slice[i] == 0 {
967                temp_buffers.push(&[]);
968            } else {
969                let buffer =
970                    unsafe { ::std::slice::from_raw_parts(values_slice[i], sizes_slice[i]) };
971                temp_buffers.push(buffer);
972            }
973        }
974
975        match dispatch_by_instance_type!(instance, Self, set_binary, value_refs, &temp_buffers) {
976            Ok(_) => binding::fmi3Status_fmi3OK,
977            Err(_) => binding::fmi3Status_fmi3Error,
978        }
979    }
980
981    // Clock functions
982    #[inline(always)]
983    unsafe fn fmi3_get_clock(
984        instance: binding::fmi3Instance,
985        value_references: *const binding::fmi3ValueReference,
986        n_value_references: usize,
987        values: *mut binding::fmi3Clock,
988    ) -> binding::fmi3Status {
989        let value_refs =
990            unsafe { ::std::slice::from_raw_parts(value_references, n_value_references) };
991        let values_slice = unsafe { ::std::slice::from_raw_parts_mut(values, n_value_references) };
992        match dispatch_by_instance_type!(instance, Self, get_clock, value_refs, values_slice) {
993            Ok(res) => {
994                let status: Fmi3Status = res.into();
995                status.into()
996            }
997            Err(_) => binding::fmi3Status_fmi3Error,
998        }
999    }
1000
1001    #[inline(always)]
1002    unsafe fn fmi3_set_clock(
1003        instance: binding::fmi3Instance,
1004        value_references: *const binding::fmi3ValueReference,
1005        n_value_references: usize,
1006        values: *const binding::fmi3Clock,
1007    ) -> binding::fmi3Status {
1008        let value_refs =
1009            unsafe { ::std::slice::from_raw_parts(value_references, n_value_references) };
1010        let values_slice = unsafe { ::std::slice::from_raw_parts(values, n_value_references) };
1011        match dispatch_by_instance_type!(instance, Self, set_clock, value_refs, values_slice) {
1012            Ok(res) => {
1013                let status: Fmi3Status = res.into();
1014                status.into()
1015            }
1016            Err(_) => binding::fmi3Status_fmi3Error,
1017        }
1018    }
1019
1020    // Variable Dependency functions
1021    #[inline(always)]
1022    unsafe fn fmi3_get_number_of_variable_dependencies(
1023        instance: binding::fmi3Instance,
1024        value_reference: binding::fmi3ValueReference,
1025        n_dependencies: *mut usize,
1026    ) -> binding::fmi3Status {
1027        match dispatch_by_instance_type!(
1028            instance,
1029            Self,
1030            get_number_of_variable_dependencies,
1031            value_reference
1032        ) {
1033            Ok(res) => {
1034                unsafe {
1035                    *n_dependencies = res;
1036                }
1037                binding::fmi3Status_fmi3OK
1038            }
1039            Err(_) => binding::fmi3Status_fmi3Error,
1040        }
1041    }
1042
1043    #[inline(always)]
1044    unsafe fn fmi3_get_variable_dependencies(
1045        instance: binding::fmi3Instance,
1046        dependent: binding::fmi3ValueReference,
1047        element_indices_of_dependent: *mut usize,
1048        independents: *mut binding::fmi3ValueReference,
1049        element_indices_of_independents: *mut usize,
1050        dependency_kinds: *mut binding::fmi3DependencyKind,
1051        n_dependencies: usize,
1052    ) -> binding::fmi3Status {
1053        // Convert the value reference to our trait's ValueRef type
1054        let dependent_vr = dependent.into();
1055
1056        // Call the Rust method to get dependencies
1057        match dispatch_by_instance_type!(instance, Self, get_variable_dependencies, dependent_vr) {
1058            Ok(dependencies) => {
1059                // Check if the caller provided enough space
1060                if dependencies.len() > n_dependencies {
1061                    eprintln!(
1062                        "Buffer too small: {} dependencies returned but only {} allocated",
1063                        dependencies.len(),
1064                        n_dependencies
1065                    );
1066                    return binding::fmi3Status_fmi3Error;
1067                }
1068
1069                // Copy dependency data to the C arrays
1070                for (i, dep) in dependencies.iter().enumerate() {
1071                    if i >= n_dependencies {
1072                        break; // Safety check
1073                    }
1074
1075                    unsafe {
1076                        // Set element index of dependent
1077                        *element_indices_of_dependent.add(i) = dep.dependent_element_index;
1078
1079                        // Set independent value reference
1080                        *independents.add(i) = dep.independent.into();
1081
1082                        // Set element index of independent
1083                        *element_indices_of_independents.add(i) = dep.independent_element_index;
1084
1085                        // Set dependency kind
1086                        *dependency_kinds.add(i) = dep.dependency_kind;
1087                    }
1088                }
1089
1090                binding::fmi3Status_fmi3OK
1091            }
1092            Err(e) => {
1093                eprintln!("Failed to get variable dependencies: {:?}", e);
1094                Fmi3Status::from(e).into()
1095            }
1096        }
1097    }
1098}
1099
1100// Model Exchange trait
1101pub trait Fmi3ModelExchange: Fmi3Common + ModelGetSetStates
1102where
1103    ModelInstance<Self, BasicContext<Self>>: fmi::fmi3::ModelExchange,
1104{
1105    #[inline(always)]
1106    unsafe fn fmi3_enter_continuous_time_mode(
1107        instance: binding::fmi3Instance,
1108    ) -> binding::fmi3Status {
1109        match dispatch_by_instance_type!(instance, Self, enter_continuous_time_mode) {
1110            Ok(res) => {
1111                let status: Fmi3Status = res.into();
1112                status.into()
1113            }
1114            Err(_) => binding::fmi3Status_fmi3Error,
1115        }
1116    }
1117
1118    #[inline(always)]
1119    unsafe fn fmi3_completed_integrator_step(
1120        instance: binding::fmi3Instance,
1121        no_set_fmu_state_prior: binding::fmi3Boolean,
1122        enter_event_mode: *mut binding::fmi3Boolean,
1123        terminate_simulation: *mut binding::fmi3Boolean,
1124    ) -> binding::fmi3Status {
1125        let mut enter_event = false;
1126        let mut terminate = false;
1127        match dispatch_by_instance_type!(
1128            instance,
1129            Self,
1130            completed_integrator_step,
1131            no_set_fmu_state_prior,
1132            &mut enter_event,
1133            &mut terminate
1134        ) {
1135            Ok(_) => {
1136                unsafe {
1137                    *enter_event_mode = enter_event;
1138                    *terminate_simulation = terminate;
1139                }
1140                binding::fmi3Status_fmi3OK
1141            }
1142            Err(_) => binding::fmi3Status_fmi3Error,
1143        }
1144    }
1145
1146    #[inline(always)]
1147    unsafe fn fmi3_set_time(
1148        instance: binding::fmi3Instance,
1149        time: binding::fmi3Float64,
1150    ) -> binding::fmi3Status {
1151        match dispatch_by_instance_type!(instance, Self, set_time, time) {
1152            Ok(res) => {
1153                let status: Fmi3Status = res.into();
1154                status.into()
1155            }
1156            Err(_) => binding::fmi3Status_fmi3Error,
1157        }
1158    }
1159
1160    #[inline(always)]
1161    unsafe fn fmi3_set_continuous_states(
1162        instance: binding::fmi3Instance,
1163        continuous_states: *const binding::fmi3Float64,
1164        n_continuous_states: usize,
1165    ) -> binding::fmi3Status {
1166        let states =
1167            unsafe { ::std::slice::from_raw_parts(continuous_states, n_continuous_states) };
1168        match dispatch_by_instance_type!(instance, Self, set_continuous_states, states) {
1169            Ok(res) => {
1170                let status: Fmi3Status = res.into();
1171                status.into()
1172            }
1173            Err(_) => binding::fmi3Status_fmi3Error,
1174        }
1175    }
1176
1177    #[inline(always)]
1178    unsafe fn fmi3_get_continuous_state_derivatives(
1179        instance: binding::fmi3Instance,
1180        derivatives: *mut binding::fmi3Float64,
1181        n_continuous_states: usize,
1182    ) -> binding::fmi3Status {
1183        let derivs = unsafe { ::std::slice::from_raw_parts_mut(derivatives, n_continuous_states) };
1184        match dispatch_by_instance_type!(instance, Self, get_continuous_state_derivatives, derivs) {
1185            Ok(res) => {
1186                let status: Fmi3Status = res.into();
1187                status.into()
1188            }
1189            Err(_) => binding::fmi3Status_fmi3Error,
1190        }
1191    }
1192
1193    #[inline(always)]
1194    unsafe fn fmi3_get_event_indicators(
1195        instance: binding::fmi3Instance,
1196        event_indicators: *mut binding::fmi3Float64,
1197        n_event_indicators: usize,
1198    ) -> binding::fmi3Status {
1199        let indicators =
1200            unsafe { ::std::slice::from_raw_parts_mut(event_indicators, n_event_indicators) };
1201        match dispatch_by_instance_type!(instance, Self, get_event_indicators, indicators) {
1202            Ok(_) => binding::fmi3Status_fmi3OK,
1203            Err(_) => binding::fmi3Status_fmi3Error,
1204        }
1205    }
1206
1207    #[inline(always)]
1208    unsafe fn fmi3_get_continuous_states(
1209        instance: binding::fmi3Instance,
1210        continuous_states: *mut binding::fmi3Float64,
1211        n_continuous_states: usize,
1212    ) -> binding::fmi3Status {
1213        let states =
1214            unsafe { ::std::slice::from_raw_parts_mut(continuous_states, n_continuous_states) };
1215        match dispatch_by_instance_type!(instance, Self, get_continuous_states, states) {
1216            Ok(res) => {
1217                let status: Fmi3Status = res.into();
1218                status.into()
1219            }
1220            Err(_) => binding::fmi3Status_fmi3Error,
1221        }
1222    }
1223
1224    #[inline(always)]
1225    unsafe fn fmi3_get_nominals_of_continuous_states(
1226        instance: binding::fmi3Instance,
1227        nominals: *mut binding::fmi3Float64,
1228        n_continuous_states: usize,
1229    ) -> binding::fmi3Status {
1230        let nominals = unsafe { ::std::slice::from_raw_parts_mut(nominals, n_continuous_states) };
1231        match dispatch_by_instance_type!(
1232            instance,
1233            Self,
1234            get_nominals_of_continuous_states,
1235            nominals
1236        ) {
1237            Ok(res) => {
1238                let status: Fmi3Status = res.into();
1239                status.into()
1240            }
1241            Err(_) => binding::fmi3Status_fmi3Error,
1242        }
1243    }
1244
1245    #[inline(always)]
1246    unsafe fn fmi3_get_number_of_event_indicators(
1247        instance: binding::fmi3Instance,
1248        n_event_indicators: *mut usize,
1249    ) -> binding::fmi3Status {
1250        match dispatch_by_instance_type!(instance, Self, get_number_of_event_indicators) {
1251            Ok(n) => {
1252                unsafe {
1253                    *n_event_indicators = n;
1254                }
1255                binding::fmi3Status_fmi3OK
1256            }
1257            Err(_) => binding::fmi3Status_fmi3Error,
1258        }
1259    }
1260
1261    #[inline(always)]
1262    unsafe fn fmi3_get_number_of_continuous_states(
1263        instance: binding::fmi3Instance,
1264        n_continuous_states: *mut usize,
1265    ) -> binding::fmi3Status {
1266        match dispatch_by_instance_type!(instance, Self, get_number_of_continuous_states) {
1267            Ok(n) => {
1268                unsafe {
1269                    *n_continuous_states = n;
1270                }
1271                binding::fmi3Status_fmi3OK
1272            }
1273            Err(_) => binding::fmi3Status_fmi3Error,
1274        }
1275    }
1276}
1277
1278// Co-Simulation trait
1279pub trait Fmi3CoSimulation: Fmi3Common + ModelGetSetStates
1280where
1281    ModelInstance<Self, BasicContext<Self>>: fmi::fmi3::CoSimulation,
1282{
1283    #[inline(always)]
1284    unsafe fn fmi3_enter_step_mode(instance: binding::fmi3Instance) -> binding::fmi3Status {
1285        match dispatch_by_instance_type!(instance, Self, enter_step_mode) {
1286            Ok(res) => {
1287                let status: Fmi3Status = res.into();
1288                status.into()
1289            }
1290            Err(_) => binding::fmi3Status_fmi3Error,
1291        }
1292    }
1293
1294    #[inline(always)]
1295    unsafe fn fmi3_get_output_derivatives(
1296        instance: binding::fmi3Instance,
1297        value_references: *const binding::fmi3ValueReference,
1298        n_value_references: usize,
1299        orders: *const binding::fmi3Int32,
1300        values: *mut binding::fmi3Float64,
1301        n_values: usize,
1302    ) -> binding::fmi3Status {
1303        let value_refs =
1304            unsafe { ::std::slice::from_raw_parts(value_references, n_value_references) };
1305        let orders_slice = unsafe { ::std::slice::from_raw_parts(orders, n_value_references) };
1306        let values_slice = unsafe { ::std::slice::from_raw_parts_mut(values, n_values) };
1307        match dispatch_by_instance_type!(
1308            instance,
1309            Self,
1310            get_output_derivatives,
1311            value_refs,
1312            orders_slice,
1313            values_slice
1314        ) {
1315            Ok(res) => {
1316                let status: Fmi3Status = res.into();
1317                status.into()
1318            }
1319            Err(_) => binding::fmi3Status_fmi3Error,
1320        }
1321    }
1322
1323    #[inline(always)]
1324    unsafe fn fmi3_do_step(
1325        instance: binding::fmi3Instance,
1326        current_communication_point: binding::fmi3Float64,
1327        communication_step_size: binding::fmi3Float64,
1328        no_set_fmu_state_prior_to_current_point: binding::fmi3Boolean,
1329        event_handling_needed: *mut binding::fmi3Boolean,
1330        terminate_simulation: *mut binding::fmi3Boolean,
1331        early_return: *mut binding::fmi3Boolean,
1332        last_successful_time: *mut binding::fmi3Float64,
1333    ) -> binding::fmi3Status {
1334        // Handle optional output pointers gracefully; if null, write into local temps.
1335        let mut event_handling_needed_tmp = false;
1336        let mut terminate_simulation_tmp = false;
1337        let mut early_return_tmp = false;
1338        let mut last_successful_time_tmp = 0.0;
1339
1340        let event_handling_needed = unsafe {
1341            if event_handling_needed.is_null() {
1342                &mut event_handling_needed_tmp
1343            } else {
1344                &mut *event_handling_needed
1345            }
1346        };
1347        let terminate_simulation = unsafe {
1348            if terminate_simulation.is_null() {
1349                &mut terminate_simulation_tmp
1350            } else {
1351                &mut *terminate_simulation
1352            }
1353        };
1354        let early_return = unsafe {
1355            if early_return.is_null() {
1356                &mut early_return_tmp
1357            } else {
1358                &mut *early_return
1359            }
1360        };
1361        let last_successful_time = unsafe {
1362            if last_successful_time.is_null() {
1363                &mut last_successful_time_tmp
1364            } else {
1365                &mut *last_successful_time
1366            }
1367        };
1368
1369        match dispatch_by_instance_type!(
1370            instance,
1371            Self,
1372            do_step,
1373            current_communication_point,
1374            communication_step_size,
1375            no_set_fmu_state_prior_to_current_point,
1376            event_handling_needed,
1377            terminate_simulation,
1378            early_return,
1379            last_successful_time
1380        ) {
1381            Ok(res) => {
1382                let status: Fmi3Status = res.into();
1383                status.into()
1384            }
1385            Err(e) => fmi::fmi3::Fmi3Status::from(e).into(),
1386        }
1387    }
1388}
1389
1390pub trait Fmi3ScheduledExecution: Fmi3Common + ModelGetSetStates
1391where
1392    ModelInstance<Self, BasicContext<Self>>: fmi::fmi3::ScheduledExecution,
1393{
1394    #[inline(always)]
1395    unsafe fn fmi3_activate_model_partition(
1396        instance: binding::fmi3Instance,
1397        clock_reference: binding::fmi3ValueReference,
1398        activation_time: binding::fmi3Float64,
1399    ) -> binding::fmi3Status {
1400        match dispatch_by_instance_type!(
1401            instance,
1402            Self,
1403            activate_model_partition,
1404            clock_reference.into(),
1405            activation_time
1406        ) {
1407            Ok(res) => {
1408                let status: Fmi3Status = res.into();
1409                status.into()
1410            }
1411            Err(e) => fmi::fmi3::Fmi3Status::from(e).into(),
1412        }
1413    }
1414}
1415
1416// Automatic implementations for all models
1417impl<T> Fmi3Common for T where T: Model + UserModel + ModelGetSet<Self> + ModelGetSetStates + 'static
1418{}
1419
1420impl<T> Fmi3ModelExchange for T
1421where
1422    T: Model + UserModel + Fmi3Common + ModelGetSetStates + 'static,
1423    ModelInstance<T, BasicContext<T>>: fmi::fmi3::ModelExchange,
1424{
1425}
1426
1427impl<T> Fmi3CoSimulation for T
1428where
1429    T: Model + ModelGetSetStates + UserModel + Fmi3Common + 'static,
1430    ModelInstance<T, BasicContext<T>>: fmi::fmi3::CoSimulation,
1431{
1432}
1433
1434impl<T> Fmi3ScheduledExecution for T where
1435    T: Model + UserModel + ModelGetSetStates + Fmi3Common + 'static
1436{
1437}