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