fmi_test_data/
lib.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
#![doc=include_str!( "../README.md")]
#![deny(unsafe_code)]
#![deny(clippy::all)]

use anyhow::Context;
use fetch_data::{ctor, FetchData};
use fmi::{schema::MajorVersion, traits::FmiImport};
use std::{
    fs::File,
    io::{Cursor, Read},
};
use tempfile::NamedTempFile;

const REF_ARCHIVE: &str = "Reference-FMUs-0.0.29.zip";
const REF_URL: &str = "https://github.com/modelica/Reference-FMUs/releases/download/v0.0.29/";

#[ctor]
static STATIC_FETCH_DATA: FetchData = FetchData::new(
    include_str!("registry.txt"),
    REF_URL,
    "FMU_DATA_DIR",
    "org",
    "modelica",
    "reference-fmus",
);

/// A Rust interface to the Modelica Reference-FMUs, downloaded as an archive using `fetch_data`
pub struct ReferenceFmus {
    archive: zip::ZipArchive<File>,
}

impl std::fmt::Debug for ReferenceFmus {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        f.debug_struct("ReferenceFmus")
            .field("archive", &self.archive.comment())
            .finish()
    }
}

impl ReferenceFmus {
    /// Fetch the released Modelica Reference-FMUs file
    pub fn new() -> anyhow::Result<Self> {
        let path = STATIC_FETCH_DATA
            .fetch_file(REF_ARCHIVE)
            .context(format!("Fetch {REF_ARCHIVE}"))?;
        let f = std::fs::File::open(&path).context(format!("Open {:?}", path))?;
        let archive = zip::ZipArchive::new(f)?;
        Ok(Self { archive })
    }

    pub fn get_reference_fmu<Imp: FmiImport>(&mut self, name: &str) -> anyhow::Result<Imp> {
        let version = Imp::MAJOR_VERSION.to_string();
        let mut f = self.archive.by_name(&format!("{version}/{name}.fmu"))?;
        // Read f into a Vec<u8> that can be used to create a new Import
        let mut buf = Vec::new();
        f.read_to_end(buf.as_mut())?;
        Ok(fmi::import::new(Cursor::new(buf))?)
    }

    /// Extract a reference FMU from the reference archive into a temporary file
    pub fn extract_reference_fmu(
        &mut self,
        name: &str,
        version: MajorVersion,
    ) -> anyhow::Result<NamedTempFile> {
        let version = version.to_string();
        let filename = format!("{version}/{name}.fmu");
        let mut fin = self.archive.by_name(&filename).context("Open {filename}")?;
        let mut fout = tempfile::NamedTempFile::new()?;
        std::io::copy(fin.by_ref(), fout.as_file_mut())
            .context("Extracting {path:?} to tempfile")?;
        Ok(fout)
    }
}

#[test]
fn test_reference_fmus() {
    use fmi::traits::FmiImport;
    let mut reference_fmus = ReferenceFmus::new().unwrap();
    let fmu: fmi::fmi2::import::Fmi2Import =
        reference_fmus.get_reference_fmu("BouncingBall").unwrap();
    assert_eq!(fmu.model_description().fmi_version, "2.0");
    assert_eq!(fmu.model_description().model_name, "BouncingBall");
    let fmu: fmi::fmi3::import::Fmi3Import =
        reference_fmus.get_reference_fmu("BouncingBall").unwrap();
    assert_eq!(fmu.model_description().fmi_version, "3.0");
    assert_eq!(fmu.model_description().model_name, "BouncingBall");
}

#[cfg(feature = "disabled")]
#[test]
fn print_registry_contents() {
    let registry_contents = STATIC_FETCH_DATA
        .gen_registry_contents([REF_ARCHIVE])
        .unwrap();
    println!("{registry_contents}");
}