238 lines
6.8 KiB
Rust
238 lines
6.8 KiB
Rust
//! 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<f32>,
|
|
/// Damage field as flattened array [Z, Y, X]
|
|
pub damage_field: Vec<f32>,
|
|
/// 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<TissueCount>,
|
|
}
|
|
|
|
/// 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<String>,
|
|
}
|
|
|
|
/// 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<f32>,
|
|
/// 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<f32>,
|
|
/// Damage values as flattened 2D array
|
|
pub damage: Vec<f32>,
|
|
/// Tissue labels as flattened 2D array
|
|
pub tissue: Vec<u8>,
|
|
/// 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<SimulationConfig>,
|
|
/// Current geometry summary
|
|
pub geometry_summary: Option<GeometrySummary>,
|
|
/// Whether simulation is running
|
|
pub is_simulating: bool,
|
|
/// Last simulation result
|
|
pub last_result: Option<SimulationResult>,
|
|
/// Last what-if result
|
|
pub last_what_if: Option<WhatIfResult>,
|
|
/// Current probe configuration
|
|
pub current_probe: Option<ProbeConfig>,
|
|
}
|
|
|
|
#[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);
|
|
}
|
|
}
|