Skip to main content

fmi_export/fmi3/instance/
mod.rs

1use fmi::fmi3::{Fmi3Error, Fmi3Status, binding};
2
3use crate::fmi3::{
4    ModelState, UserModel,
5    traits::{Context, Model},
6};
7
8mod common;
9pub mod context;
10mod get_set;
11mod impl_cs;
12mod impl_me;
13mod impl_se;
14
15pub type LogMessageClosure = Box<dyn Fn(Fmi3Status, &str, std::fmt::Arguments<'_>) + Send + Sync>;
16pub type IntermediateUpdateClosure =
17    Box<dyn Fn(f64, bool, bool, bool, bool) -> Option<f64> + Send + Sync>;
18
19/// An exportable FMU instance, generic over model type M and context type C
20#[repr(C)]
21pub struct ModelInstance<M, C>
22where
23    M: UserModel,
24    C: Context<M>,
25{
26    /// The instance type (public for FFI access)
27    pub(crate) instance_type: fmi::InterfaceType,
28    /// The name of this instance
29    instance_name: String,
30    /// Context for the model instance
31    context: C,
32    /// Current state of the model instance
33    state: ModelState,
34    /// Do we need to re-evaluate the model equations?
35    is_dirty_values: bool,
36    /// The user-defined model
37    model: M,
38}
39
40impl<M, C> ModelInstance<M, C>
41where
42    M: Model + UserModel,
43    C: Context<M>,
44{
45    pub fn new(
46        name: String,
47        instantiation_token: &str,
48        context: C,
49        instance_type: fmi::InterfaceType,
50    ) -> Result<Self, Fmi3Error> {
51        // Validate the instantiation token using the compile-time constant
52        if instantiation_token != M::INSTANTIATION_TOKEN {
53            eprintln!(
54                "Instantiation token mismatch. Expected: '{}', got: '{}'",
55                M::INSTANTIATION_TOKEN,
56                instantiation_token
57            );
58            return Err(Fmi3Error::Error);
59        }
60
61        let mut instance = Self {
62            instance_name: name,
63            context,
64            state: ModelState::Instantiated,
65            instance_type,
66            is_dirty_values: true,
67            model: M::default(),
68        };
69
70        // Set start values for the model
71        instance.model.set_start_values();
72
73        Ok(instance)
74    }
75
76    pub fn instance_name(&self) -> &str {
77        &self.instance_name
78    }
79
80    pub fn instance_type(&self) -> fmi::InterfaceType {
81        self.instance_type
82    }
83
84    pub fn context(&self) -> &C {
85        &self.context
86    }
87
88    #[inline]
89    pub fn assert_instance_type(&self, expected: fmi::InterfaceType) -> Result<(), Fmi3Error> {
90        if self.instance_type != expected {
91            self.context.log(
92                Fmi3Error::Error.into(),
93                M::LoggingCategory::default(),
94                format_args!(
95                    "Instance type mismatch. Expected: {:?}, got: {:?}",
96                    expected, self.instance_type
97                ),
98            );
99            return Err(Fmi3Error::Error);
100        }
101        Ok(())
102    }
103
104    /// Validate that a variable can be set in the current model state
105    fn validate_variable_setting(&self, vr: binding::fmi3ValueReference) -> Result<(), Fmi3Error> {
106        match M::validate_variable_setting(vr, &self.state) {
107            Ok(()) => Ok(()),
108            Err(message) => {
109                self.context.log(
110                    Fmi3Error::Error.into(),
111                    M::LoggingCategory::default(),
112                    format_args!("Variable setting error for VR {vr}: {message}"),
113                );
114                Err(Fmi3Error::Error)
115            }
116        }
117    }
118}