//! IPC message types for Digital Twin demo. use serde::{Deserialize, Serialize}; use crate::config::{InterventionType, ProbeConfig, SimulationConfig, TissueType}; /// Simulation progress update. #[derive(Debug, Clone, Serialize, Deserialize)] pub struct SimulationProgress { /// Current simulation time in seconds pub current_time: f32, /// Total simulation duration in seconds pub total_duration: f32, /// Current iteration pub iteration: u32, /// Total iterations pub total_iterations: u32, /// Current maximum temperature in °C pub max_temperature: f32, /// Current damaged volume in mm³ pub damaged_volume: f32, /// Residual (convergence metric) pub residual: f32, /// Solver status message pub status: String, } impl Default for SimulationProgress { fn default() -> Self { Self { current_time: 0.0, total_duration: 0.0, iteration: 0, total_iterations: 0, max_temperature: 37.0, damaged_volume: 0.0, residual: 0.0, status: "Initializing".to_string(), } } } /// Simulation result. #[derive(Debug, Clone, Serialize, Deserialize)] pub struct SimulationResult { /// Temperature field as flattened array [Z, Y, X] pub temperature_field: Vec, /// Damage field as flattened array [Z, Y, X] pub damage_field: Vec, /// Grid dimensions [x, y, z] pub dimensions: [u32; 3], /// Maximum temperature reached in °C pub max_temperature: f32, /// Total damaged volume in mm³ pub total_damaged_volume: f32, /// Severe damage volume in mm³ (damage > 4.6) pub severe_damage_volume: f32, /// Simulation time in seconds pub simulation_time: f32, /// Number of iterations pub iterations: u32, /// Safety margin satisfied pub safety_ok: bool, } /// What-if analysis result. #[derive(Debug, Clone, Serialize, Deserialize)] pub struct WhatIfResult { /// Intervention type used pub intervention_type: InterventionType, /// Power setting in Watts pub power: f32, /// Duration in seconds pub duration: f32, /// Maximum temperature in °C pub max_temperature: f32, /// Total damaged volume in mm³ pub total_damaged_volume: f32, /// Severe damage volume in mm³ pub severe_damage_volume: f32, /// Moderate damage volume in mm³ pub moderate_damage_volume: f32, /// Maximum boundary temperature in °C pub max_boundary_temperature: f32, /// Safety margin satisfied pub safety_ok: bool, /// Recommendation text pub recommendation: String, } /// Geometry summary. #[derive(Debug, Clone, Serialize, Deserialize)] pub struct GeometrySummary { /// Grid shape [x, y, z] pub shape: [u32; 3], /// Voxel spacing in mm [dx, dy, dz] pub spacing: [f32; 3], /// Physical dimensions in mm [x, y, z] pub dimensions: [f32; 3], /// Total number of voxels pub total_voxels: u32, /// Number of tissue voxels (non-air) pub tissue_voxels: u32, /// Total tissue volume in mm³ pub tissue_volume: f32, /// Tissue type histogram pub tissue_histogram: Vec, } /// Tissue type count for histogram. #[derive(Debug, Clone, Serialize, Deserialize)] pub struct TissueCount { /// Tissue type pub tissue_type: TissueType, /// Number of voxels pub count: u32, /// Percentage of total pub percentage: f32, } /// Request to create a new digital twin. #[derive(Debug, Clone, Serialize, Deserialize)] pub struct CreateTwinRequest { /// Simulation configuration pub config: SimulationConfig, /// Optional preset geometry (e.g., "`liver_tumor`", "kidney") pub preset: Option, } /// Request to run simulation. #[derive(Debug, Clone, Serialize, Deserialize)] pub struct RunSimulationRequest { /// Probe configuration pub probe: ProbeConfig, /// Simulation duration override (uses config default if None) pub duration: Option, /// Run steady-state instead of transient pub steady_state: bool, } /// Request for what-if analysis. #[derive(Debug, Clone, Serialize, Deserialize)] pub struct WhatIfRequest { /// Probe configuration pub probe: ProbeConfig, /// Simulation duration pub duration: f32, } /// Slice data for 2D visualization. #[derive(Debug, Clone, Serialize, Deserialize)] pub struct SliceData { /// Slice orientation pub orientation: SliceOrientation, /// Slice index pub index: u32, /// Temperature values as flattened 2D array pub temperature: Vec, /// Damage values as flattened 2D array pub damage: Vec, /// Tissue labels as flattened 2D array pub tissue: Vec, /// Slice dimensions [width, height] pub dimensions: [u32; 2], } /// Slice orientation. #[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default)] #[serde(rename_all = "snake_case")] pub enum SliceOrientation { /// XY plane (axial) #[default] Axial, /// XZ plane (coronal) Coronal, /// YZ plane (sagittal) Sagittal, } /// Demo state for UI synchronization. #[derive(Debug, Clone, Serialize, Deserialize, Default)] pub struct DigitalTwinDemoState { /// Whether twin is initialized pub twin_initialized: bool, /// Current configuration pub config: Option, /// Current geometry summary pub geometry_summary: Option, /// Whether simulation is running pub is_simulating: bool, /// Last simulation result pub last_result: Option, /// Last what-if result pub last_what_if: Option, /// Current probe configuration pub current_probe: Option, } #[cfg(test)] mod tests { use super::*; #[test] fn test_simulation_progress_default() { let progress = SimulationProgress::default(); assert_eq!(progress.max_temperature, 37.0); assert_eq!(progress.current_time, 0.0); } #[test] fn test_demo_state_default() { let state = DigitalTwinDemoState::default(); assert!(!state.twin_initialized); assert!(!state.is_simulating); } #[test] fn test_serialization() { let result = WhatIfResult { intervention_type: InterventionType::RadiofrequencyAblation, power: 50.0, duration: 60.0, max_temperature: 85.0, total_damaged_volume: 1500.0, severe_damage_volume: 800.0, moderate_damage_volume: 700.0, max_boundary_temperature: 42.0, safety_ok: true, recommendation: "Proceed with treatment".to_string(), }; let json = serde_json::to_string(&result).unwrap(); let deserialized: WhatIfResult = serde_json::from_str(&json).unwrap(); assert_eq!(deserialized.power, 50.0); assert!(deserialized.safety_ok); } }