# 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 <model.json> -i <io.json> \
  -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 <model.json> -i <io.json> \
  -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 <model.json> -i <io.json> \
  -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 <model.json> -i <io.json> \
  -t x86_64-pc-windows-msvc -o target/model_win64.fmu
```

Le dossier `binaries/<platform>/` 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.<triple>] 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/<platform>/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.
