Entropyk/README_STORY_1_3.md

356 lines
9.6 KiB
Markdown

# Entropyk - Story 1.3: Port and Connection System
## 🎯 Ce qui a été implémenté
Cette story implémente le système de ports et connexions pour les composants thermodynamiques avec le **Type-State pattern** pour la sécurité à la compilation.
### Fonctionnalités principales
-`Port<State>` - Structure générique avec états `Disconnected` et `Connected`
-`FluidId` - Identification des fluides pour validation de compatibilité
-`ConnectionError` - Gestion d'erreurs avec `thiserror`
- ✅ Validation des connexions (compatibilité fluide, continuité pression/enthalpie)
- ✅ Extension du trait `Component` avec `get_ports()`
## 🚀 Instructions de test
### 1. Prérequis
```bash
# Vérifier que Rust est installé
rustc --version
cargo --version
```
### 2. Cloner/Naviguer dans le projet
```bash
cd /Users/sepehr/dev/Entropyk
```
### 3. Compiler le projet
```bash
# Compiler tout le workspace
cargo build --workspace
# Compiler en mode release (optimisé)
cargo build --workspace --release
```
### 4. Exécuter tous les tests
```bash
# Tests complets du workspace
cargo test --workspace
# Tests avec sortie détaillée
cargo test --workspace -- --nocapture
# Tests du crate components uniquement
cargo test -p entropyk-components
# Tests du crate core uniquement
cargo test -p entropyk-core
```
### 5. Vérifier la qualité du code
```bash
# Clippy (linting strict)
cargo clippy --workspace -- -D warnings
# Formatage du code
cargo fmt --workspace
# Vérifier la documentation
cargo doc --workspace --open
```
## 🧪 Tests manuels
### Test 1: Création et connexion de ports
Créez un fichier `test_ports.rs` à la racine du projet :
```rust
use entropyk_components::port::{Port, Disconnected, Connected, FluidId, ConnectionError};
use entropyk_core::{Pressure, Enthalpy};
fn main() -> Result<(), ConnectionError> {
println!("=== Test de création de ports ===");
// Créer deux ports déconnectés
let port1 = Port::new(
FluidId::new("R134a"),
Pressure::from_bar(1.0),
Enthalpy::from_joules_per_kg(400_000.0)
);
let port2 = Port::new(
FluidId::new("R134a"),
Pressure::from_bar(1.0),
Enthalpy::from_joules_per_kg(400_000.0)
);
println!("Port 1 créé: fluid={:?}, pressure={:.2} Pa, enthalpy={:.2} J/kg",
port1.fluid_id(),
port1.pressure().to_pascals(),
port1.enthalpy().to_joules_per_kg()
);
println!("Port 2 créé: fluid={:?}, pressure={:.2} Pa, enthalpy={:.2} J/kg",
port2.fluid_id(),
port2.pressure().to_pascals(),
port2.enthalpy().to_joules_per_kg()
);
// Connecter les ports
println!("\n=== Connexion des ports ===");
let (mut connected1, mut connected2) = port1.connect(port2)?;
println!("✅ Ports connectés avec succès!");
println!("Connected 1: pressure={:.2} Pa, enthalpy={:.2} J/kg",
connected1.pressure().to_pascals(),
connected1.enthalpy().to_joules_per_kg()
);
// Modifier les valeurs
println!("\n=== Modification des valeurs ===");
connected1.set_pressure(Pressure::from_bar(1.5));
connected1.set_enthalpy(Enthalpy::from_joules_per_kg(450_000.0));
println!("Port 1 modifié: pressure={:.2} Pa, enthalpy={:.2} J/kg",
connected1.pressure().to_pascals(),
connected1.enthalpy().to_joules_per_kg()
);
Ok(())
}
```
Pour l'exécuter, ajoutez ce binary dans `Cargo.toml`:
```toml
[[bin]]
name = "test_ports"
path = "test_ports.rs"
```
Puis:
```bash
cargo run --bin test_ports
```
### Test 2: Test d'erreurs
```rust
use entropyk_components::port::{Port, FluidId, ConnectionError};
use entropyk_core::{Pressure, Enthalpy};
fn main() {
println!("=== Test des erreurs de connexion ===\n");
// Test 1: Fluides incompatibles
println!("Test 1: Fluides incompatibles");
let port1 = Port::new(
FluidId::new("R134a"),
Pressure::from_pascals(100_000.0),
Enthalpy::from_joules_per_kg(400_000.0)
);
let port2 = Port::new(
FluidId::new("Water"),
Pressure::from_pascals(100_000.0),
Enthalpy::from_joules_per_kg(400_000.0)
);
match port1.connect(port2) {
Err(ConnectionError::IncompatibleFluid { from, to }) => {
println!("✅ Erreur capturée: Cannot connect {} to {}", from, to);
}
_ => println!("❌ Erreur non capturée!"),
}
// Test 2: Pression différente
println!("\nTest 2: Pression différente");
let port3 = Port::new(
FluidId::new("R134a"),
Pressure::from_pascals(100_000.0),
Enthalpy::from_joules_per_kg(400_000.0)
);
let port4 = Port::new(
FluidId::new("R134a"),
Pressure::from_pascals(200_000.0),
Enthalpy::from_joules_per_kg(400_000.0)
);
match port3.connect(port4) {
Err(ConnectionError::PressureMismatch { from_pressure, to_pressure }) => {
println!("✅ Erreur capturée: Pressure mismatch {} vs {}",
from_pressure, to_pressure);
}
_ => println!("❌ Erreur non capturée!"),
}
// Test 3: Connexion réussie
println!("\nTest 3: Connexion réussie");
let port5 = Port::new(
FluidId::new("R134a"),
Pressure::from_pascals(100_000.0),
Enthalpy::from_joules_per_kg(400_000.0)
);
let port6 = Port::new(
FluidId::new("R134a"),
Pressure::from_pascals(100_000.0),
Enthalpy::from_joules_per_kg(400_000.0)
);
match port5.connect(port6) {
Ok(_) => println!("✅ Connexion réussie!"),
Err(_) => println!("❌ Connexion échouée!"),
}
}
```
### Test 3: Vérification Type-State (doit échouer à la compilation)
Créez ce fichier pour vérifier que la sécurité à la compilation fonctionne:
```rust
use entropyk_components::port::{Port, Disconnected};
use entropyk_core::{Pressure, Enthalpy, FluidId};
fn main() {
// Créer un port déconnecté
let port: Port<Disconnected> = Port::new(
FluidId::new("R134a"),
Pressure::from_bar(1.0),
Enthalpy::from_joules_per_kg(400_000.0)
);
// Cette ligne doit provoquer une erreur de compilation:
// error[E0599]: no method named `pressure` found for struct `Port<Disconnected>`
// let _p = port.pressure();
println!("Si vous décommentez la ligne ci-dessus, la compilation échouera!");
println!("Cela prouve que le Type-State pattern fonctionne.");
}
```
## 📊 Structure des fichiers
```
entropyk/
├── Cargo.toml # Workspace root
├── crates/
│ ├── components/ # Crate components (modifié)
│ │ ├── Cargo.toml # + Dépendances ajoutées
│ │ └── src/
│ │ ├── lib.rs # + Trait Component étendu
│ │ └── port.rs # NOUVEAU: Implémentation ports
│ └── core/ # Crate core (existant)
│ └── src/
│ └── types.rs # Pressure, Enthalpy, etc.
└── _bmad-output/
└── implementation-artifacts/
└── 1-3-port-and-connection-system.md # Story document
```
## 🔍 Points clés de l'implémentation
### Type-State Pattern
```rust
// Disconnected et Connected sont des marqueurs de type vides
pub struct Disconnected;
pub struct Connected;
// Port est générique sur l'état
pub struct Port<State> {
fluid_id: FluidId,
pressure: Pressure,
enthalpy: Enthalpy,
_state: PhantomData<State>, // Marqueur zéro-cost
}
// Seuls les ports Disconnected peuvent être connectés
impl Port<Disconnected> {
pub fn connect(self, other: Port<Disconnected>)
-> Result<(Port<Connected>, Port<Connected>), ConnectionError> {
// Validation et connexion...
}
}
// Seuls les ports Connected exposent les méthodes de lecture/écriture
impl Port<Connected> {
pub fn pressure(&self) -> Pressure { self.pressure }
pub fn set_pressure(&mut self, pressure: Pressure) { self.pressure = pressure }
}
```
### Validation des connexions
```rust
pub fn connect(self, other: Port<Disconnected>)
-> Result<(Port<Connected>, Port<Connected>), ConnectionError>
{
// 1. Validation du fluide
if self.fluid_id != other.fluid_id {
return Err(ConnectionError::IncompatibleFluid { ... });
}
// 2. Validation de la continuité de pression
if pressure_diff > 1e-6 {
return Err(ConnectionError::PressureMismatch { ... });
}
// 3. Validation de la continuité d'enthalpie
if enthalpy_diff > 1e-6 {
return Err(ConnectionError::EnthalpyMismatch { ... });
}
// Création des ports connectés avec valeurs moyennées
Ok((connected1, connected2))
}
```
## 🐛 Dépannage
### Erreur: `unresolved import entropyk_core`
Solution: Vérifier que la dépendance est bien dans `crates/components/Cargo.toml`:
```toml
[dependencies]
entropyk-core = { path = "../core" }
```
### Erreur: `approx crate not found`
Solution: Vérifier la dev-dependency:
```toml
[dev-dependencies]
approx = "0.5"
```
### Tests qui échouent
```bash
# Nettoyer et reconstruire
cargo clean
cargo test --workspace
```
## 📚 Ressources
- [Rust Type-State Pattern](https://rust-unofficial.github.io/patterns/patterns/behavioural/phantom-types.html)
- [thiserror documentation](https://docs.rs/thiserror/)
- [approx documentation](https://docs.rs/approx/) (pour les assertions flottantes)
---
**Date d'implémentation**: 2026-02-14
**Story**: 1.3 - Port and Connection System
**Statut**: ✅ Complété et testé