fmi 0.7.0

A Rust interface to FMUs (Functional Mockup Units) that follow the FMI Standard. See http://www.fmi-standard.org/
Documentation
use std::path::PathBuf;

use fmi_schema::{MajorVersion, traits::FmiModelDescription};
use tempfile::TempDir;

use crate::{
    Error,
    fmi3::{Fmi3Model, binding, instance, schema},
    traits::FmiImport,
};

/// FMU import for FMI 3.0
#[derive(Debug)]
pub struct Fmi3Import {
    /// Path to the unzipped FMU on disk
    dir: tempfile::TempDir,
    /// Parsed raw-schema model description
    model_description: schema::Fmi3ModelDescription,
}

impl FmiImport for Fmi3Import {
    const MAJOR_VERSION: MajorVersion = MajorVersion::FMI3;
    type ModelDescription = schema::Fmi3ModelDescription;
    type Binding = binding::Fmi3Binding;
    type ValueRef = binding::fmi3ValueReference;

    /// Create a new FMI 3.0 import from a directory containing the unzipped FMU
    fn new(dir: TempDir, schema_xml: &str) -> Result<Self, Error> {
        let model_description = schema::Fmi3ModelDescription::deserialize(schema_xml)?;
        Ok(Self {
            dir,
            model_description,
        })
    }

    #[inline]
    fn archive_path(&self) -> &std::path::Path {
        self.dir.path()
    }

    /// Get the path to the shared library
    fn shared_lib_path(&self, model_identifier: &str) -> Result<PathBuf, Error> {
        use std::env::consts::{ARCH, OS};
        let folder = super::platform_folder(OS, ARCH)?;
        let fname = format!("{model_identifier}{}", std::env::consts::DLL_SUFFIX);
        Ok(std::path::PathBuf::from("binaries")
            .join(folder)
            .join(fname))
    }

    /// Get the parsed raw-schema model description
    fn model_description(&self) -> &Self::ModelDescription {
        &self.model_description
    }

    /// Load the plugin shared library and return the raw bindings.
    fn binding(&self, model_identifier: &str) -> Result<Self::Binding, Error> {
        let lib_path = self
            .dir
            .path()
            .join(self.shared_lib_path(model_identifier)?);
        log::debug!("Loading shared library {lib_path:?}");
        unsafe { binding::Fmi3Binding::new(lib_path).map_err(Error::from) }
    }

    /// Get a `String` representation of the resources path for this FMU
    ///
    /// As per the FMI3.0 standard, `resourcePath` is the absolute file path (with a trailing file separator) of the
    /// resources directory of the extracted FMU archive.
    fn canonical_resource_path_string(&self) -> String {
        std::path::absolute(self.resource_path())
            .expect("Invalid resource path")
            .to_str()
            .expect("Invalid resource path")
            .to_owned()
    }
}

impl Fmi3Model for Fmi3Import {
    type InstanceCS = instance::InstanceCS;
    type InstanceME = instance::InstanceME;
    type InstanceSE = instance::InstanceSE;

    /// Build a derived model description from the raw-schema model description
    #[cfg(false)]
    pub fn model(&self) -> &model::ModelDescription {
        &self.model
    }

    /// Create a new instance of the FMU for Model-Exchange
    ///
    /// See [`instance::InstanceME::new`] for more information.
    fn instantiate_me(
        &self,
        instance_name: &str,
        visible: bool,
        logging_on: bool,
    ) -> Result<Self::InstanceME, Error> {
        instance::InstanceME::new(self, instance_name, visible, logging_on)
    }

    /// Create a new instance of the FMU for Co-Simulation
    ///
    /// See [`instance::InstanceCS::new`] for more information.
    fn instantiate_cs(
        &self,
        instance_name: &str,
        visible: bool,
        logging_on: bool,
        event_mode_used: bool,
        early_return_allowed: bool,
        required_intermediate_variables: &[binding::fmi3ValueReference],
    ) -> Result<Self::InstanceCS, Error> {
        instance::InstanceCS::new(
            self,
            instance_name,
            visible,
            logging_on,
            event_mode_used,
            early_return_allowed,
            required_intermediate_variables,
        )
    }

    /// Create a new instance of the FMU for Scheduled Execution
    ///
    /// See [`instance::InstanceSE::new`] for more information.
    fn instantiate_se(
        &self,
        instance_name: &str,
        visible: bool,
        logging_on: bool,
    ) -> Result<Self::InstanceSE, Error> {
        instance::InstanceSE::new(self, instance_name, visible, logging_on)
    }
}