# entropyk-fmi — Export FMI 2.0 Co-Simulation (FMU) Compile un modèle Entropyk (JSON) en une archive `.fmu` conforme FMI 2.0 Co-Simulation, embarquable sur un PLC / contrôleur industriel (Beckhoff TwinCAT, B&R, Siemens TIA, Codesys) ou testable sur PC avec fmpy / OpenModelica. ## Principe - Le moteur Entropyk (Rust) est compilé en `cdylib` et exporte l'ABI C standard `fmi2*`. - Le modèle JSON + la carte d'entrées/sorties (`fmu_io.json`) sont **embarqués dans le binaire** à la build (codegen) — aucun accès fichier au runtime. - L'hôte (PLC) pilote le scheduler temps réel ; chaque `fmi2DoStep` re-resout le cycle stationnaire aux entrées courantes (co-simulation quasi-stationnaire). ## Générer un FMU ```bash # Cible hôte (PC courant) — pour test avec fmpy cargo run -p entropyk-cli -- export-fmu \ -c crates/cli/examples/chiller_aircooled_r134a.json \ -i bindings/fmi/examples/chiller_aircooled_io.json \ -t host \ -o target/chiller.fmu ``` ### Cross-compilation (arm / x86) Installer les cibles Rust voulues, puis : ```bash # Linux x86_64 rustup target add x86_64-unknown-linux-gnu cargo run -p entropyk-cli -- export-fmu -c -i \ -t x86_64-unknown-linux-gnu -o target/model_linux64.fmu # Linux ARM64 (aarch64) rustup target add aarch64-unknown-linux-gnu cargo run -p entropyk-cli -- export-fmu -c -i \ -t aarch64-unknown-linux-gnu -o target/model_aarch64.fmu # Linux ARM32 (Cortex-A, hard-float) rustup target add arm-unknown-linux-gnueabihf cargo run -p entropyk-cli -- export-fmu -c -i \ -t arm-unknown-linux-gnueabihf -o target/model_arm32.fmu # Windows x86_64 (PLC TwinCAT sur IPC Windows) rustup target add x86_64-pc-windows-msvc cargo run -p entropyk-cli -- export-fmu -c -i \ -t x86_64-pc-windows-msvc -o target/model_win64.fmu ``` Le dossier `binaries//` est choisi automatiquement depuis le triple cible : | Triple cible | Dossier FMI | |--------------|-------------| | `x86_64-unknown-linux-gnu` / `musl` | `linux64` | | `aarch64-unknown-linux-gnu` / `musl` | `linuxaarch64` | | `arm-unknown-linux-gnueabihf` | `linuxarm32` | | `x86_64-pc-windows-msvc` / `gnu` | `win64` | | `x86_64-apple-darwin` / `aarch64-apple-darwin` | `darwin64` | > Cross-compiler Linux depuis Windows : installez les linkers croisés (ex. `aarch64-linux-gnu-gcc`) et configurez `[target.] linker = "..."` dans `~/.cargo/config.toml`. Le codegen du crate staged hérite de la config cargo de l'utilisateur. ## Contenu de l'archive `.fmu` ``` modelDescription.xml (déclare les entrées/sorties, le GUID, le modelIdentifier) binaries//entropyk_fmi.{so|dll|dylib} resources/config.json (le modèle Entropyk, pour référence) resources/fmu_io.json (la carte d'entrées/sorties) ``` ## Carte d'entrées/sorties (`fmu_io.json`) Déclare quelles variables le PLC peut écrire (entrées) et lire (sorties). Les `valueReference` sont attribués dans l'ordre : entrées d'abord (`0..n_inputs`), puis sorties (`n_inputs..`). ```json { "inputs": [ { "name": "T_air_c", "component": "cond_air_in", "param": "t_dry_c", "kind": "input" }, { "name": "m_air", "component": "cond_air_in", "param": "m_flow_kg_s", "kind": "input" } ], "outputs": [ { "name": "COP", "kind": "cop" }, { "name": "Q_cool_kW", "kind": "q_cooling_kw" }, { "name": "P_kW", "kind": "compressor_power_kw" }, { "name": "P_evap_bar","kind": "pressure_bar", "edge": 3 } ] } ``` `kind` pour les sorties : `cop`, `q_cooling_kw`, `q_heating_kw`, `compressor_power_kw`, `pressure_bar`, `enthalpy_kj_kg`, `mass_flow_kg_s`. Les trois derniers nécessitent `edge` (index d'arête). ## Mode dev (sans codegen) Pour itérer sans recompiler le FMU à chaque changement de modèle, le runtime peut lire le modèle depuis des variables d'environnement au lieu de la version embarquée : ```bash cargo build -p entropyk-fmi export ENTROPYK_FMU_MODEL=crates/cli/examples/chiller_aircooled_r134a.json export ENTROPYK_FMU_IO=bindings/fmi/examples/chiller_aircooled_io.json # charger target/debug/entropyk_fmi.{so|dll} dans fmpy comme FMU Co-Sim ``` ## Test rapide avec fmpy (Python) ```bash uv pip install fmpy uv run python -c "from fmpy import simulate_fmu; simulate_fmu('target/chiller.fmu', stop_time=1.0)" ``` ## Limites actuelles (skeleton) - **Pas de warm-start** : chaque `doStep` reconstruit le graphe `System` et relance un Newton à froid. Pour un cycle 100 ms c'est acceptable ; l'optimisation (réutiliser l'état précédent + le `System` construit) nécessite de scinder `execute_simulation` en `build_system` + `solve_warm` — TODO. - **Backend fluide** : le FMU embarque actuellement le backend déclaré dans le JSON (CoolProp inclus). Pour un petit MCU, préférer le backend **tabulaire** (tables précalculées hors-ligne) ; vérifier la couverture de la plage de fonctionnement. - **Itérations bornées** : à configurer via `solver.max_iterations` dans le modèle pour garantir un budget temps réel par pas.