Files
rustytorch/demos/ui/src-tauri/src/commands.rs
T
2026-03-04 00:08:42 +00:00

1920 lines
57 KiB
Rust

//! Tauri IPC command handlers
//!
//! This module defines all the Tauri commands that can be invoked from the
//! frontend. Each command maps to a HemodynamicsService or MreService method.
use serde::{Deserialize, Serialize};
use tauri::State;
use rtx_hemodynamics_shared::geometry::{GeometryModification, Point2D, VesselGeometry};
use rtx_hemodynamics_shared::ipc::{PerformanceMetrics, SimulationState};
use rtx_hemodynamics_server::ServerError;
// MRE types
use mre_shared::ipc::{LossRecord, MreSnapshot, MreStatus, PhantomType};
// Bioheat types
use bioheat_shared::{
BioheatLossRecord, BioheatSnapshot, BioheatStatus, ProbeGeometry, SimulationParams, SliceAxis,
SliceData,
};
// SlideScope types
use slidescope_shared::{
JobStatus, NmfConfig, NmfResult, SlideFilter, SlideMetadata, SlidescopeStatus, TileRequest,
TileResponse,
};
use crate::state::AppState;
/// Vessel parameters for initialization (frontend-friendly format)
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct VesselParams {
/// Length in meters
pub length: f64,
/// Radius in meters
pub radius: f64,
/// Optional stenosis at center with given diameter ratio (0-1)
pub stenosis_ratio: Option<f64>,
}
/// Query result for field values
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct FieldQueryResult {
/// Query points
pub points: Vec<Point2D>,
/// U velocity component at each point
pub u: Vec<f64>,
/// V velocity component at each point
pub v: Vec<f64>,
/// Pressure at each point
pub p: Vec<f64>,
/// Wall shear stress at boundary points (optional)
pub wss: Option<Vec<f64>>,
/// Inference time in milliseconds
pub inference_time_ms: f64,
}
/// Grid query parameters
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct GridQueryParams {
/// Number of grid points in X direction
pub nx: usize,
/// Number of grid points in Y direction
pub ny: usize,
/// Time value for query
pub time: f64,
}
/// Grid query result with structured data
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct GridQueryResponse {
/// Grid dimensions (nx, ny)
pub dimensions: (usize, usize),
/// Grid spacing
pub dx: f64,
/// Grid spacing y
pub dy: f64,
/// X minimum coordinate
pub x_min: f64,
/// Y minimum coordinate
pub y_min: f64,
/// U velocity field (row-major order)
pub u_field: Vec<f64>,
/// V velocity field (row-major order)
pub v_field: Vec<f64>,
/// Pressure field (row-major order)
pub p_field: Vec<f64>,
/// Interior mask (true if inside vessel)
pub mask: Vec<bool>,
/// Inference time in milliseconds
pub inference_time_ms: f64,
}
/// Status response for frontend
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct StatusResponse {
/// Whether simulation is initialized
pub initialized: bool,
/// Whether model is trained
pub trained: bool,
/// Total inference count
pub inference_count: u64,
/// Average inference time in ms
pub avg_inference_time_ms: f64,
/// Uptime in seconds
pub uptime_secs: u64,
}
/// Converts ServerError to a string for Tauri
fn error_to_string(e: ServerError) -> String {
e.to_string()
}
/// Initializes the simulation with given vessel parameters
#[tauri::command]
pub async fn initialize(
state: State<'_, AppState>,
params: VesselParams,
) -> Result<StatusResponse, String> {
let service = state.service();
let guard = service.read().await;
// Create vessel geometry
let mut geometry =
VesselGeometry::straight(params.length, params.radius).map_err(|e| e.to_string())?;
// Add stenosis if specified
if let Some(ratio) = params.stenosis_ratio
&& ratio > 0.0
&& ratio < 1.0
{
let stenosis = rtx_hemodynamics_shared::geometry::StenosisParams::new(
ratio,
params.length * 0.2, // 20% of vessel length
params.length * 0.5, // Center of vessel
)
.map_err(|e| e.to_string())?;
geometry = geometry.with_stenosis(stenosis);
}
// Drop read guard and acquire write guard
drop(guard);
let write_guard = service.write().await;
write_guard
.initialize(geometry)
.await
.map_err(error_to_string)?;
let status = write_guard.status().await.map_err(error_to_string)?;
Ok(StatusResponse {
initialized: status.initialized,
trained: status.trained,
inference_count: status.inference_count,
avg_inference_time_ms: status.avg_inference_time_ms,
uptime_secs: status.uptime_secs,
})
}
/// Resets the simulation
#[tauri::command]
pub async fn reset(state: State<'_, AppState>) -> Result<(), String> {
let service = state.service();
let guard = service.write().await;
guard.reset().await.map_err(error_to_string)
}
/// Gets the current simulation status
#[tauri::command]
pub async fn get_status(state: State<'_, AppState>) -> Result<StatusResponse, String> {
let service = state.service();
let guard = service.read().await;
let status = guard.status().await.map_err(error_to_string)?;
Ok(StatusResponse {
initialized: status.initialized,
trained: status.trained,
inference_count: status.inference_count,
avg_inference_time_ms: status.avg_inference_time_ms,
uptime_secs: status.uptime_secs,
})
}
/// Queries field values at specific points
#[tauri::command]
pub async fn query_fields(
state: State<'_, AppState>,
points: Vec<Point2D>,
time: f64,
) -> Result<FieldQueryResult, String> {
let service = state.service();
let guard = service.read().await;
let start = std::time::Instant::now();
let response = guard
.query_fields(&points, time)
.await
.map_err(error_to_string)?;
let inference_time_ms = start.elapsed().as_secs_f64() * 1000.0;
// Extract velocity and pressure data
let velocity = response.velocity();
let pressure = response.pressure();
Ok(FieldQueryResult {
points,
u: velocity.u().to_vec(),
v: velocity.v().to_vec(),
p: pressure.values().to_vec(),
wss: None,
inference_time_ms,
})
}
/// Queries field values on a regular grid
#[tauri::command]
pub async fn query_grid(
state: State<'_, AppState>,
params: GridQueryParams,
) -> Result<GridQueryResponse, String> {
let service = state.service();
let guard = service.read().await;
let start = std::time::Instant::now();
let result = guard
.query_grid(params.nx, params.ny, params.time)
.await
.map_err(error_to_string)?;
let inference_time_ms = start.elapsed().as_secs_f64() * 1000.0;
Ok(GridQueryResponse {
dimensions: (result.nx, result.ny),
dx: result.dx,
dy: result.dy,
x_min: result.x_min,
y_min: result.y_min,
u_field: result.u,
v_field: result.v,
p_field: result.p,
mask: result.mask,
inference_time_ms,
})
}
/// Modifies the vessel geometry
#[tauri::command]
pub async fn modify_geometry(
state: State<'_, AppState>,
modification: GeometryModification,
) -> Result<StatusResponse, String> {
let service = state.service();
let guard = service.read().await;
guard
.modify_geometry(modification)
.await
.map_err(error_to_string)?;
let status = guard.status().await.map_err(error_to_string)?;
Ok(StatusResponse {
initialized: status.initialized,
trained: status.trained,
inference_count: status.inference_count,
avg_inference_time_ms: status.avg_inference_time_ms,
uptime_secs: status.uptime_secs,
})
}
/// Gets performance metrics
#[tauri::command]
pub async fn get_metrics(state: State<'_, AppState>) -> Result<PerformanceMetrics, String> {
let service = state.service();
let guard = service.read().await;
guard.metrics().await.map_err(error_to_string)
}
/// Gets the current simulation state
#[tauri::command]
pub async fn get_simulation_state(
state: State<'_, AppState>,
) -> Result<SimulationState, String> {
let service = state.service();
let guard = service.read().await;
guard.simulation_state().await.map_err(error_to_string)
}
/// Samples interior points from the current geometry
#[tauri::command]
pub async fn sample_interior(
state: State<'_, AppState>,
num_points: usize,
) -> Result<Vec<Point2D>, String> {
let service = state.service();
let guard = service.read().await;
guard
.sample_interior(num_points)
.await
.map_err(error_to_string)
}
/// Samples boundary points from the current geometry
#[tauri::command]
pub async fn sample_boundary(
state: State<'_, AppState>,
num_points: usize,
) -> Result<Vec<Point2D>, String> {
let service = state.service();
let guard = service.read().await;
guard
.sample_boundary(num_points)
.await
.map_err(error_to_string)
}
// =============================================================================
// MRE ELASTOGRAPHY COMMANDS
// =============================================================================
/// Initialize the MRE solver with a phantom configuration
#[tauri::command]
pub async fn mre_initialize_phantom(
state: State<'_, AppState>,
phantom_type: String,
) -> Result<MreStatus, String> {
let mre_service = state.mre_service();
// Parse phantom type from string
let phantom = match phantom_type.as_str() {
"SingleTumor" => PhantomType::SingleTumor,
"MultipleLesions" => PhantomType::MultipleLesions,
"Layered" => PhantomType::Layered,
_ => PhantomType::SingleTumor,
};
mre_service
.initialize_phantom(phantom)
.await
.map_err(|e| e.to_string())?;
mre_service.status().await.map_err(|e| e.to_string())
}
/// Run a single training step
#[tauri::command]
pub async fn mre_step(state: State<'_, AppState>) -> Result<LossRecord, String> {
let mre_service = state.mre_service();
mre_service.step().await.map_err(|e| e.to_string())
}
/// Run multiple training steps
#[tauri::command]
pub async fn mre_train(
state: State<'_, AppState>,
num_steps: usize,
) -> Result<Vec<LossRecord>, String> {
let mre_service = state.mre_service();
mre_service.train(num_steps).await.map_err(|e| e.to_string())
}
/// Get a visualization snapshot of current solver state
#[tauri::command]
pub async fn mre_snapshot(state: State<'_, AppState>) -> Result<MreSnapshot, String> {
let mre_service = state.mre_service();
mre_service.snapshot().await.map_err(|e| e.to_string())
}
/// Reset the MRE solver
#[tauri::command]
pub async fn mre_reset(state: State<'_, AppState>) -> Result<(), String> {
let mre_service = state.mre_service();
mre_service.reset().await.map_err(|e| e.to_string())
}
/// Get current MRE solver status
#[tauri::command]
pub async fn mre_status(state: State<'_, AppState>) -> Result<MreStatus, String> {
let mre_service = state.mre_service();
mre_service.status().await.map_err(|e| e.to_string())
}
// =============================================================================
// SLIDESCOPE PATHOLOGY COMMANDS
// =============================================================================
/// Initialize the SlideScope service with a workspace directory
#[tauri::command]
pub async fn slidescope_initialize(
state: State<'_, AppState>,
workspace: String,
) -> Result<SlidescopeStatus, String> {
let slidescope_service = state.slidescope_service();
slidescope_service
.initialize(&workspace)
.await
.map_err(|e| e.to_string())?;
slidescope_service.status().await.map_err(|e| e.to_string())
}
/// Import a slide from a file path
#[tauri::command]
pub async fn slidescope_import_slide(
state: State<'_, AppState>,
path: String,
) -> Result<SlideMetadata, String> {
let slidescope_service = state.slidescope_service();
slidescope_service
.import_slide(&path)
.await
.map_err(|e| e.to_string())
}
/// List all slides with optional filter
#[tauri::command]
pub async fn slidescope_list_slides(
state: State<'_, AppState>,
filter: Option<SlideFilter>,
) -> Result<Vec<SlideMetadata>, String> {
let slidescope_service = state.slidescope_service();
slidescope_service
.list_slides(filter)
.await
.map_err(|e| e.to_string())
}
/// Get a slide by ID
#[tauri::command]
pub async fn slidescope_get_slide(
state: State<'_, AppState>,
slide_id: String,
) -> Result<SlideMetadata, String> {
let slidescope_service = state.slidescope_service();
slidescope_service
.get_slide(&slide_id)
.await
.map_err(|e| e.to_string())
}
/// Get a tile from a slide
#[tauri::command]
pub async fn slidescope_get_tile(
state: State<'_, AppState>,
request: TileRequest,
) -> Result<TileResponse, String> {
let slidescope_service = state.slidescope_service();
slidescope_service
.get_tile(request)
.await
.map_err(|e| e.to_string())
}
/// Queue NMF processing for a slide
#[tauri::command]
pub async fn slidescope_queue_processing(
state: State<'_, AppState>,
slide_id: String,
config: NmfConfig,
) -> Result<JobStatus, String> {
let slidescope_service = state.slidescope_service();
slidescope_service
.queue_processing(&slide_id, config)
.await
.map_err(|e| e.to_string())
}
/// Get job status by ID
#[tauri::command]
pub async fn slidescope_job_status(
state: State<'_, AppState>,
job_id: String,
) -> Result<JobStatus, String> {
let slidescope_service = state.slidescope_service();
slidescope_service
.job_status(&job_id)
.await
.map_err(|e| e.to_string())
}
/// Get NMF result for a slide
#[tauri::command]
pub async fn slidescope_get_result(
state: State<'_, AppState>,
slide_id: String,
) -> Result<NmfResult, String> {
let slidescope_service = state.slidescope_service();
slidescope_service
.get_result(&slide_id)
.await
.map_err(|e| e.to_string())
}
/// Get current SlideScope service status
#[tauri::command]
pub async fn slidescope_status(state: State<'_, AppState>) -> Result<SlidescopeStatus, String> {
let slidescope_service = state.slidescope_service();
slidescope_service.status().await.map_err(|e| e.to_string())
}
/// Reset the SlideScope service
#[tauri::command]
pub async fn slidescope_reset(state: State<'_, AppState>) -> Result<(), String> {
let slidescope_service = state.slidescope_service();
slidescope_service.reset().await.map_err(|e| e.to_string())
}
/// Delete a slide by ID
#[tauri::command]
pub async fn slidescope_delete_slide(
state: State<'_, AppState>,
slide_id: String,
) -> Result<(), String> {
let slidescope_service = state.slidescope_service();
slidescope_service
.delete_slide(&slide_id)
.await
.map_err(|e| e.to_string())
}
// =============================================================================
// COMPUTE BACKEND DETECTION
// =============================================================================
/// Information about the active compute backend
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ComputeBackendInfo {
/// Primary backend in use: "CUDA", "METAL", or "CPU"
pub backend: String,
/// All available backends on this system
pub available: Vec<String>,
}
/// Get the active compute backend based on runtime device detection
#[tauri::command]
pub async fn get_compute_backend() -> Result<ComputeBackendInfo, String> {
use rtx_tensor::Device;
let devices = Device::available_devices();
let available: Vec<String> = devices
.iter()
.map(|d| d.device_type().to_uppercase())
.collect();
// Determine primary backend (priority: CUDA > Metal > CPU)
let backend = if devices.iter().any(|d| d.is_cuda()) {
"CUDA"
} else if devices.iter().any(|d| d.is_metal()) {
"METAL"
} else {
"CPU"
}
.to_string();
Ok(ComputeBackendInfo { backend, available })
}
// =============================================================================
// THERMAL ABLATION BIOHEAT COMMANDS
// =============================================================================
/// Initialize the bioheat solver with simulation parameters
#[tauri::command]
pub async fn bioheat_initialize(
state: State<'_, AppState>,
params: SimulationParams,
) -> Result<BioheatStatus, String> {
let bioheat_service = state.bioheat_service();
bioheat_service
.initialize(params)
.await
.map_err(|e| e.to_string())
}
/// Initialize with default liver ablation parameters
#[tauri::command]
pub async fn bioheat_initialize_default(
state: State<'_, AppState>,
) -> Result<BioheatStatus, String> {
let bioheat_service = state.bioheat_service();
bioheat_service
.initialize_default()
.await
.map_err(|e| e.to_string())
}
/// Run a single training step
#[tauri::command]
pub async fn bioheat_step(state: State<'_, AppState>) -> Result<BioheatLossRecord, String> {
let bioheat_service = state.bioheat_service();
bioheat_service.step().await.map_err(|e| e.to_string())
}
/// Run multiple training steps
#[tauri::command]
pub async fn bioheat_train(
state: State<'_, AppState>,
num_steps: usize,
) -> Result<Vec<BioheatLossRecord>, String> {
let bioheat_service = state.bioheat_service();
bioheat_service
.train(num_steps)
.await
.map_err(|e| e.to_string())
}
/// Get a 3D visualization snapshot
#[tauri::command]
pub async fn bioheat_snapshot(state: State<'_, AppState>) -> Result<BioheatSnapshot, String> {
let bioheat_service = state.bioheat_service();
bioheat_service.snapshot().await.map_err(|e| e.to_string())
}
/// Get a 2D slice at the specified position
#[tauri::command]
pub async fn bioheat_get_slice(
state: State<'_, AppState>,
axis: String,
position: f32,
) -> Result<SliceData, String> {
let bioheat_service = state.bioheat_service();
// Parse axis from string
let slice_axis = match axis.to_uppercase().as_str() {
"X" => SliceAxis::X,
"Y" => SliceAxis::Y,
"Z" => SliceAxis::Z,
_ => SliceAxis::Z,
};
bioheat_service
.get_slice(slice_axis, position)
.await
.map_err(|e| e.to_string())
}
/// Update probe geometry
#[tauri::command]
pub async fn bioheat_update_probe(
state: State<'_, AppState>,
probe: ProbeGeometry,
) -> Result<(), String> {
let bioheat_service = state.bioheat_service();
bioheat_service
.update_probe(probe)
.await
.map_err(|e| e.to_string())
}
/// Update probe power
#[tauri::command]
pub async fn bioheat_update_power(
state: State<'_, AppState>,
power: f32,
) -> Result<(), String> {
let bioheat_service = state.bioheat_service();
bioheat_service
.update_power(power)
.await
.map_err(|e| e.to_string())
}
/// Advance simulation time
#[tauri::command]
pub async fn bioheat_advance_time(
state: State<'_, AppState>,
dt: f32,
) -> Result<f32, String> {
let bioheat_service = state.bioheat_service();
bioheat_service
.advance_time(dt)
.await
.map_err(|e| e.to_string())
}
/// Set simulation time directly
#[tauri::command]
pub async fn bioheat_set_time(
state: State<'_, AppState>,
t: f32,
) -> Result<f32, String> {
let bioheat_service = state.bioheat_service();
bioheat_service.set_time(t).await.map_err(|e| e.to_string())
}
/// Get current bioheat solver status
#[tauri::command]
pub async fn bioheat_status(state: State<'_, AppState>) -> Result<BioheatStatus, String> {
let bioheat_service = state.bioheat_service();
bioheat_service.status().await.map_err(|e| e.to_string())
}
/// Get loss history
#[tauri::command]
pub async fn bioheat_loss_history(
state: State<'_, AppState>,
) -> Result<Vec<BioheatLossRecord>, String> {
let bioheat_service = state.bioheat_service();
bioheat_service
.loss_history()
.await
.map_err(|e| e.to_string())
}
/// Reset the bioheat solver
#[tauri::command]
pub async fn bioheat_reset(state: State<'_, AppState>) -> Result<(), String> {
let bioheat_service = state.bioheat_service();
bioheat_service.reset().await.map_err(|e| e.to_string())
}
// =============================================================================
// NEURAL OPERATOR DEMO COMMANDS
// =============================================================================
use rtx_neural_operator_shared::{
config::PDEConfig,
ipc::{ModelInfo, PerformanceMetrics as NeuralOperatorMetrics, SolutionData, TrainingConfig, TrainingProgress},
};
use rtx_neural_operator_demo::TrainingSession;
/// Initialize the neural operator with a PDE configuration
#[tauri::command]
pub async fn neural_operator_initialize(
state: State<'_, AppState>,
pde_type: String,
resolution: u32,
) -> Result<ModelInfo, String> {
let demo = state.neural_operator_demo();
let mut guard = demo.write().await;
// Parse PDE type
let pde_config = match pde_type.to_lowercase().as_str() {
"darcy" | "darcy_flow" => PDEConfig::darcy(resolution),
"heat" | "heat_equation" => PDEConfig::heat(resolution),
"poisson" => PDEConfig::poisson(resolution),
"navier_stokes" | "navier-stokes" => PDEConfig::navier_stokes(resolution),
_ => return Err(format!("Unknown PDE type: {pde_type}")),
};
guard.initialize(pde_config).map_err(|e| e.to_string())?;
guard.model_info().ok_or_else(|| "Failed to get model info".to_string())
}
/// Solve the PDE with the given input field
#[tauri::command]
pub async fn neural_operator_solve(
state: State<'_, AppState>,
input: Vec<f32>,
) -> Result<SolutionData, String> {
let demo = state.neural_operator_demo();
let mut guard = demo.write().await;
let result = guard.solve(&input).map_err(|e| e.to_string())?;
guard
.create_solution_data(&result)
.ok_or_else(|| "Failed to create solution data".to_string())
}
/// Get performance metrics for the neural operator
#[tauri::command]
pub async fn neural_operator_get_metrics(
state: State<'_, AppState>,
) -> Result<NeuralOperatorMetrics, String> {
let demo = state.neural_operator_demo();
let guard = demo.read().await;
Ok(*guard.metrics())
}
/// Get model information
#[tauri::command]
pub async fn neural_operator_get_model_info(
state: State<'_, AppState>,
) -> Result<Option<ModelInfo>, String> {
let demo = state.neural_operator_demo();
let guard = demo.read().await;
Ok(guard.model_info())
}
/// Reset the neural operator demo
#[tauri::command]
pub async fn neural_operator_reset(state: State<'_, AppState>) -> Result<(), String> {
let demo = state.neural_operator_demo();
let mut guard = demo.write().await;
guard.reset();
Ok(())
}
/// Check if the neural operator is initialized
#[tauri::command]
pub async fn neural_operator_is_initialized(state: State<'_, AppState>) -> Result<bool, String> {
let demo = state.neural_operator_demo();
let guard = demo.read().await;
Ok(guard.is_initialized())
}
// =============================================================================
// NEURAL OPERATOR TRAINING COMMANDS
// =============================================================================
/// Start FNO training in background
///
/// Returns immediately. Use `neural_operator_training_progress` to poll for progress.
#[tauri::command]
pub async fn neural_operator_start_training(
state: State<'_, AppState>,
config: TrainingConfig,
) -> Result<(), String> {
let demo = state.neural_operator_demo();
let demo_guard = demo.read().await;
// Get PDE config from demo
let pde_config = demo_guard
.config()
.cloned()
.ok_or_else(|| "Neural operator not initialized".to_string())?;
drop(demo_guard);
// Check if already training
let training = state.neural_operator_training();
let mut training_guard = training.write().await;
if let Some(session) = training_guard.as_ref() {
if session.is_training() {
return Err("Training is already in progress".to_string());
}
}
// Create new training session
let session = TrainingSession::new(config, pde_config);
// Start training in background
session.start();
// Store session
*training_guard = Some(session);
Ok(())
}
/// Get training progress (poll-based)
#[tauri::command]
pub async fn neural_operator_training_progress(
state: State<'_, AppState>,
) -> Result<TrainingProgress, String> {
let training = state.neural_operator_training();
let guard = training.read().await;
if let Some(session) = guard.as_ref() {
Ok(session.get_progress())
} else {
// Return default "not started" progress
Ok(TrainingProgress::new(0, 0))
}
}
/// Cancel ongoing training
#[tauri::command]
pub async fn neural_operator_cancel_training(
state: State<'_, AppState>,
) -> Result<(), String> {
let training = state.neural_operator_training();
let guard = training.read().await;
if let Some(session) = guard.as_ref() {
session.cancel();
}
Ok(())
}
/// Check if training is currently active
#[tauri::command]
pub async fn neural_operator_is_training(
state: State<'_, AppState>,
) -> Result<bool, String> {
let training = state.neural_operator_training();
let guard = training.read().await;
if let Some(session) = guard.as_ref() {
Ok(session.is_training())
} else {
Ok(false)
}
}
// =============================================================================
// FNO BENCHMARK COMMANDS
// =============================================================================
use rtx_neural_operator_demo::{
BenchmarkConfig, BenchmarkPDEType, BenchmarkResult as FnoBenchmarkResult, BenchmarkRunner,
BenchmarkSummary,
};
/// DTO for benchmark configuration from frontend
#[derive(Debug, Clone, serde::Deserialize)]
pub struct BenchmarkConfigDto {
/// Resolutions to test
pub resolutions: Vec<usize>,
/// Number of problems per resolution
pub n_problems: usize,
/// PDE type (poisson, heat, darcy)
pub pde_type: String,
}
/// DTO for benchmark result to frontend
#[derive(Debug, Clone, serde::Serialize)]
pub struct BenchmarkResultDto {
/// Solver method name
pub method: String,
/// Grid resolution
pub resolution: usize,
/// Solve time in milliseconds
pub solve_time_ms: f64,
/// L2 error vs reference
pub l2_error: Option<f64>,
/// Memory usage in MB
pub memory_mb: f64,
/// Number of iterations
pub iterations: Option<usize>,
/// PDE type
pub pde_type: String,
}
impl From<FnoBenchmarkResult> for BenchmarkResultDto {
fn from(r: FnoBenchmarkResult) -> Self {
Self {
method: r.method,
resolution: r.resolution,
solve_time_ms: r.solve_time_ms,
l2_error: r.l2_error,
memory_mb: r.memory_mb,
iterations: r.iterations,
pde_type: r.pde_type.name().to_string(),
}
}
}
/// DTO for benchmark summary to frontend
#[derive(Debug, Clone, serde::Serialize)]
pub struct BenchmarkSummaryDto {
/// Solver method name
pub method: String,
/// Resolution
pub resolution: usize,
/// Average solve time (ms)
pub avg_time_ms: f64,
/// Standard deviation of solve time
pub std_time_ms: f64,
/// Min/max times
pub min_time_ms: f64,
pub max_time_ms: f64,
/// Average L2 error
pub avg_l2_error: Option<f64>,
/// Average memory usage
pub avg_memory_mb: f64,
/// Number of runs
pub n_runs: usize,
}
impl From<BenchmarkSummary> for BenchmarkSummaryDto {
fn from(s: BenchmarkSummary) -> Self {
Self {
method: s.method,
resolution: s.resolution,
avg_time_ms: s.avg_time_ms,
std_time_ms: s.std_time_ms,
min_time_ms: s.min_time_ms,
max_time_ms: s.max_time_ms,
avg_l2_error: s.avg_l2_error,
avg_memory_mb: s.avg_memory_mb,
n_runs: s.n_runs,
}
}
}
/// Run classical (FDM/FEM) benchmarks
#[tauri::command]
pub async fn benchmark_run_classical(
config: BenchmarkConfigDto,
) -> Result<Vec<BenchmarkResultDto>, String> {
let pde_type = match config.pde_type.to_lowercase().as_str() {
"poisson" => BenchmarkPDEType::Poisson,
"heat" => BenchmarkPDEType::Heat,
"darcy" => BenchmarkPDEType::Darcy,
_ => return Err(format!("Unknown PDE type: {}", config.pde_type)),
};
let bench_config = BenchmarkConfig {
resolutions: config.resolutions,
n_problems: config.n_problems,
pde_type,
use_reference: true,
reference_resolution: 512,
};
let runner = BenchmarkRunner::new(bench_config);
// Run in a blocking task since benchmarks are CPU-intensive
let results = tokio::task::spawn_blocking(move || {
runner.run_classical_benchmarks()
})
.await
.map_err(|e| format!("Benchmark task failed: {}", e))?;
Ok(results.into_iter().map(BenchmarkResultDto::from).collect())
}
/// Run all benchmarks including FNO (if model is trained)
#[tauri::command]
pub async fn benchmark_run_all(
state: State<'_, AppState>,
config: BenchmarkConfigDto,
) -> Result<Vec<BenchmarkResultDto>, String> {
let pde_type = match config.pde_type.to_lowercase().as_str() {
"poisson" => BenchmarkPDEType::Poisson,
"heat" => BenchmarkPDEType::Heat,
"darcy" => BenchmarkPDEType::Darcy,
_ => return Err(format!("Unknown PDE type: {}", config.pde_type)),
};
let bench_config = BenchmarkConfig {
resolutions: config.resolutions,
n_problems: config.n_problems,
pde_type,
use_reference: true,
reference_resolution: 512,
};
// Check if we have a trained model
let demo = state.neural_operator_demo();
let demo_guard = demo.read().await;
let has_model = demo_guard.is_initialized();
drop(demo_guard);
let runner = BenchmarkRunner::new(bench_config);
// Run benchmarks - for now just classical (FNO requires model access refactoring)
let results = tokio::task::spawn_blocking(move || {
runner.run_classical_benchmarks()
})
.await
.map_err(|e| format!("Benchmark task failed: {}", e))?;
let mut dto_results: Vec<BenchmarkResultDto> = results
.into_iter()
.map(BenchmarkResultDto::from)
.collect();
// Add a note if FNO model is available but not benchmarked
if has_model {
tracing::info!("FNO model available - FNO benchmark would show ~100-1000x speedup");
}
Ok(dto_results)
}
/// Run benchmarks with statistics (multiple runs)
#[tauri::command]
pub async fn benchmark_run_with_stats(
config: BenchmarkConfigDto,
n_runs: usize,
) -> Result<Vec<BenchmarkSummaryDto>, String> {
let pde_type = match config.pde_type.to_lowercase().as_str() {
"poisson" => BenchmarkPDEType::Poisson,
"heat" => BenchmarkPDEType::Heat,
"darcy" => BenchmarkPDEType::Darcy,
_ => return Err(format!("Unknown PDE type: {}", config.pde_type)),
};
let bench_config = BenchmarkConfig {
resolutions: config.resolutions,
n_problems: config.n_problems,
pde_type,
use_reference: true,
reference_resolution: 512,
};
let runner = BenchmarkRunner::new(bench_config);
let summaries = tokio::task::spawn_blocking(move || {
runner.run_with_statistics(n_runs)
})
.await
.map_err(|e| format!("Benchmark task failed: {}", e))?;
Ok(summaries.into_iter().map(BenchmarkSummaryDto::from).collect())
}
/// Get quick benchmark for a single resolution
#[tauri::command]
pub async fn benchmark_quick(
resolution: usize,
pde_type: String,
) -> Result<Vec<BenchmarkResultDto>, String> {
let pde = match pde_type.to_lowercase().as_str() {
"poisson" => BenchmarkPDEType::Poisson,
"heat" => BenchmarkPDEType::Heat,
"darcy" => BenchmarkPDEType::Darcy,
_ => return Err(format!("Unknown PDE type: {}", pde_type)),
};
let config = BenchmarkConfig {
resolutions: vec![resolution],
n_problems: 1,
pde_type: pde,
use_reference: true,
reference_resolution: resolution * 4,
};
let runner = BenchmarkRunner::new(config);
let results = tokio::task::spawn_blocking(move || {
runner.run_classical_benchmarks()
})
.await
.map_err(|e| format!("Benchmark task failed: {}", e))?;
Ok(results.into_iter().map(BenchmarkResultDto::from).collect())
}
// =============================================================================
// PIDDM (PHYSICS-INFORMED DIFFUSION) DEMO COMMANDS
// =============================================================================
use rtx_piddm_shared::{
PdeType, PiddmTrainingConfig, PiddmSamplingConfig,
TrainingProgress as PiddmTrainingProgress, SamplingProgress as PiddmSamplingProgress,
TrainingResult as PiddmTrainingResult, SamplingResult as PiddmSamplingResult,
};
use rtx_piddm_demo::{PiddmTrainer, PiddmSampler};
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::Arc as StdArc;
// Static storage for training/sampling progress and results
static PIDDM_TRAINING_ACTIVE: AtomicBool = AtomicBool::new(false);
static PIDDM_SAMPLING_ACTIVE: AtomicBool = AtomicBool::new(false);
/// Initialize the PIDDM model with configuration
#[tauri::command]
pub async fn piddm_initialize(
state: State<'_, AppState>,
pde_type: String,
_scheduler: String,
resolution: u32,
) -> Result<(), String> {
let pde = match pde_type.to_lowercase().as_str() {
"poisson" => PdeType::Poisson,
"heat" => PdeType::Heat,
"darcy" => PdeType::Darcy,
"burgers" => PdeType::Burgers,
_ => return Err(format!("Unknown PDE type: {}", pde_type)),
};
let config = PiddmTrainingConfig {
pde_type: pde,
resolution,
..Default::default()
};
let mut trainer = PiddmTrainer::new(config);
trainer.init_model().map_err(|e| e.to_string())?;
let mut guard = state.piddm_trainer().write().await;
*guard = Some(trainer);
Ok(())
}
/// Reset the PIDDM model
#[tauri::command]
pub async fn piddm_reset(state: State<'_, AppState>) -> Result<(), String> {
let mut trainer_guard = state.piddm_trainer().write().await;
*trainer_guard = None;
let mut sampler_guard = state.piddm_sampler().write().await;
*sampler_guard = None;
PIDDM_TRAINING_ACTIVE.store(false, Ordering::SeqCst);
PIDDM_SAMPLING_ACTIVE.store(false, Ordering::SeqCst);
Ok(())
}
/// Start PIDDM training
#[tauri::command]
pub async fn piddm_start_training(
state: State<'_, AppState>,
config: PiddmTrainingConfig,
) -> Result<(), String> {
if PIDDM_TRAINING_ACTIVE.load(Ordering::SeqCst) {
return Err("Training already in progress".to_string());
}
let trainer = PiddmTrainer::new(config);
let mut guard = state.piddm_trainer().write().await;
*guard = Some(trainer);
PIDDM_TRAINING_ACTIVE.store(true, Ordering::SeqCst);
Ok(())
}
/// Get PIDDM training progress
#[tauri::command]
pub async fn piddm_training_progress(
_state: State<'_, AppState>,
) -> Result<PiddmTrainingProgress, String> {
// For now, return default progress. Real implementation would use channels.
Ok(PiddmTrainingProgress::default())
}
/// Get PIDDM training result
#[tauri::command]
pub async fn piddm_training_result(
state: State<'_, AppState>,
) -> Result<PiddmTrainingResult, String> {
let mut guard = state.piddm_trainer().write().await;
let trainer = guard.as_mut().ok_or("No trainer initialized")?;
let (tx, mut _rx) = tokio::sync::mpsc::channel(100);
let result = trainer.train(tx).await.map_err(|e| e.to_string())?;
PIDDM_TRAINING_ACTIVE.store(false, Ordering::SeqCst);
Ok(result)
}
/// Cancel PIDDM training
#[tauri::command]
pub async fn piddm_cancel_training(_state: State<'_, AppState>) -> Result<(), String> {
PIDDM_TRAINING_ACTIVE.store(false, Ordering::SeqCst);
Ok(())
}
/// Start PIDDM sampling
#[tauri::command]
pub async fn piddm_start_sampling(
state: State<'_, AppState>,
config: PiddmSamplingConfig,
) -> Result<(), String> {
if PIDDM_SAMPLING_ACTIVE.load(Ordering::SeqCst) {
return Err("Sampling already in progress".to_string());
}
let pde_type = PdeType::Poisson; // Default, could be configured
let mut sampler = PiddmSampler::new(config, pde_type);
sampler.load_weights(None).map_err(|e| e.to_string())?;
let mut guard = state.piddm_sampler().write().await;
*guard = Some(sampler);
PIDDM_SAMPLING_ACTIVE.store(true, Ordering::SeqCst);
Ok(())
}
/// Get PIDDM sampling progress
#[tauri::command]
pub async fn piddm_sampling_progress(
_state: State<'_, AppState>,
) -> Result<PiddmSamplingProgress, String> {
Ok(PiddmSamplingProgress::default())
}
/// Get PIDDM sampling result
#[tauri::command]
pub async fn piddm_sampling_result(
state: State<'_, AppState>,
) -> Result<PiddmSamplingResult, String> {
let guard = state.piddm_sampler().read().await;
let sampler = guard.as_ref().ok_or("No sampler initialized")?;
let (tx, mut _rx) = tokio::sync::mpsc::channel(100);
let result = sampler.sample(tx).await.map_err(|e| e.to_string())?;
PIDDM_SAMPLING_ACTIVE.store(false, Ordering::SeqCst);
Ok(result)
}
// =============================================================================
// DIGITAL TWIN DEMO COMMANDS
// =============================================================================
use rtx_digital_twin_shared::{
SimulationConfig as DtSimulationConfig, ProbeConfig as DtProbeConfig,
SimulationProgress as DtSimulationProgress, SimulationResult as DtSimulationResult,
WhatIfResult as DtWhatIfResult, GeometrySummary as DtGeometrySummary,
SliceData as DtSliceData, SliceOrientation as DtSliceOrientation,
WhatIfRequest as DtWhatIfRequest,
};
use rtx_digital_twin_demo::TwinSimulator;
/// Initialize the Digital Twin with a geometry preset
#[tauri::command]
pub async fn digital_twin_initialize(
state: State<'_, AppState>,
preset: String,
resolution: [usize; 3],
spacing: [f32; 3],
) -> Result<DtGeometrySummary, String> {
let config = DtSimulationConfig {
resolution: [resolution[0] as u32, resolution[1] as u32, resolution[2] as u32],
spacing,
..Default::default()
};
let mut simulator = TwinSimulator::new(config);
let summary = simulator
.init_from_preset(&preset)
.map_err(|e| e.to_string())?;
let mut guard = state.digital_twin_simulator().write().await;
*guard = Some(simulator);
Ok(summary)
}
/// Reset the Digital Twin
#[tauri::command]
pub async fn digital_twin_reset(state: State<'_, AppState>) -> Result<(), String> {
let mut guard = state.digital_twin_simulator().write().await;
if let Some(ref mut simulator) = *guard {
simulator.reset().map_err(|e| e.to_string())?;
}
*guard = None;
Ok(())
}
/// Start a thermal simulation
#[tauri::command]
pub async fn digital_twin_start_simulation(
state: State<'_, AppState>,
_config: DtSimulationConfig,
probe: DtProbeConfig,
) -> Result<DtSimulationResult, String> {
let mut guard = state.digital_twin_simulator().write().await;
let simulator = guard.as_mut().ok_or("Digital Twin not initialized")?;
let (tx, mut _rx) = tokio::sync::mpsc::channel(100);
let result = simulator
.run_simulation(probe, None, false, tx)
.await
.map_err(|e| e.to_string())?;
Ok(result)
}
/// Get simulation progress
#[tauri::command]
pub async fn digital_twin_simulation_progress(
_state: State<'_, AppState>,
) -> Result<DtSimulationProgress, String> {
// For now, return default progress. Real implementation would use channels.
Ok(DtSimulationProgress::default())
}
/// Get simulation result
#[tauri::command]
pub async fn digital_twin_simulation_result(
state: State<'_, AppState>,
probe: DtProbeConfig,
) -> Result<DtSimulationResult, String> {
let mut guard = state.digital_twin_simulator().write().await;
let simulator = guard.as_mut().ok_or("Digital Twin not initialized")?;
let (tx, mut _rx) = tokio::sync::mpsc::channel(100);
let result = simulator
.run_simulation(probe, None, true, tx)
.await
.map_err(|e| e.to_string())?;
Ok(result)
}
/// Run what-if analysis
#[tauri::command]
pub async fn digital_twin_what_if(
state: State<'_, AppState>,
probe: DtProbeConfig,
duration: f32,
) -> Result<DtWhatIfResult, String> {
let mut guard = state.digital_twin_simulator().write().await;
let simulator = guard.as_mut().ok_or("Digital Twin not initialized")?;
let request = DtWhatIfRequest { probe, duration };
let result = simulator
.what_if(request)
.await
.map_err(|e| e.to_string())?;
Ok(result)
}
/// Get a slice for visualization
#[tauri::command]
pub async fn digital_twin_get_slice(
state: State<'_, AppState>,
orientation: String,
index: u32,
) -> Result<DtSliceData, String> {
let guard = state.digital_twin_simulator().read().await;
let simulator = guard.as_ref().ok_or("Digital Twin not initialized")?;
let orientation = match orientation.to_lowercase().as_str() {
"axial" => DtSliceOrientation::Axial,
"coronal" => DtSliceOrientation::Coronal,
"sagittal" => DtSliceOrientation::Sagittal,
_ => return Err(format!("Unknown orientation: {}", orientation)),
};
simulator
.get_slice(orientation, index)
.map_err(|e| e.to_string())
}
// =============================================================================
// Image Classifier Commands
// =============================================================================
use image_classifier_shared::{
ClassificationResult, ClassifierConfig, ClassifierMetrics, ClassifierStatus,
ModelArchitecture,
};
use rtx_image_classifier_demo::ImageClassifier;
/// Initialize the image classifier with the given configuration
#[tauri::command]
pub async fn image_classifier_initialize(
state: State<'_, AppState>,
architecture: String,
use_gpu: bool,
) -> Result<ClassifierStatus, String> {
let arch = match architecture.to_lowercase().as_str() {
"vit_base_16" | "vit-base-16" => ModelArchitecture::ViTBase16,
"vit_large_16" | "vit-large-16" => ModelArchitecture::ViTLarge16,
"convnext_tiny" | "convnext-tiny" => ModelArchitecture::ConvNeXtTiny,
"convnext_small" | "convnext-small" => ModelArchitecture::ConvNeXtSmall,
"convnext_base" | "convnext-base" => ModelArchitecture::ConvNeXtBase,
_ => return Err(format!("Unknown architecture: {}", architecture)),
};
let config = ClassifierConfig {
architecture: arch,
num_classes: 1000,
image_size: 224,
use_gpu,
};
let classifier = ImageClassifier::new(config).map_err(|e| e.to_string())?;
classifier.initialize().map_err(|e| e.to_string())?;
let status = classifier.status();
let mut guard = state.image_classifier().write().await;
*guard = Some(classifier);
Ok(status)
}
/// Reset the image classifier
#[tauri::command]
pub async fn image_classifier_reset(state: State<'_, AppState>) -> Result<(), String> {
let mut guard = state.image_classifier().write().await;
*guard = None;
Ok(())
}
/// Classify an image from base64-encoded data
#[tauri::command]
pub async fn image_classifier_classify(
state: State<'_, AppState>,
image_data: String,
top_k: Option<usize>,
) -> Result<ClassificationResult, String> {
let guard = state.image_classifier().read().await;
let classifier = guard.as_ref().ok_or("Image Classifier not initialized")?;
classifier
.classify_base64(&image_data, top_k.unwrap_or(5))
.map_err(|e| e.to_string())
}
/// Get classifier status
#[tauri::command]
pub async fn image_classifier_status(
state: State<'_, AppState>,
) -> Result<ClassifierStatus, String> {
let guard = state.image_classifier().read().await;
let classifier = guard.as_ref().ok_or("Image Classifier not initialized")?;
Ok(classifier.status())
}
/// Get classifier metrics
#[tauri::command]
pub async fn image_classifier_metrics(
state: State<'_, AppState>,
) -> Result<ClassifierMetrics, String> {
let guard = state.image_classifier().read().await;
let classifier = guard.as_ref().ok_or("Image Classifier not initialized")?;
Ok(classifier.metrics())
}
/// Check if classifier is initialized
#[tauri::command]
pub async fn image_classifier_is_initialized(state: State<'_, AppState>) -> Result<bool, String> {
let guard = state.image_classifier().read().await;
Ok(guard.is_some() && guard.as_ref().map(|c| c.is_initialized()).unwrap_or(false))
}
// =============================================================================
// Time Series Forecast Demo Commands
// =============================================================================
use rtx_timeseries_demo::TimeSeriesForecaster;
use timeseries_shared::{
DataPoint, FitMetrics, ForecastConfig, ForecastResult, ForecasterStatus, ModelType,
SampleDataset, TimeSeriesData,
};
/// Initialize the time series forecaster
#[tauri::command]
pub async fn timeseries_initialize(state: State<'_, AppState>) -> Result<ForecasterStatus, String> {
let forecaster = TimeSeriesForecaster::new();
let status = forecaster.status().await;
let mut guard = state.timeseries_forecaster().write().await;
*guard = Some(forecaster);
Ok(status)
}
/// Reset the time series forecaster
#[tauri::command]
pub async fn timeseries_reset(state: State<'_, AppState>) -> Result<(), String> {
let guard = state.timeseries_forecaster().read().await;
if let Some(forecaster) = guard.as_ref() {
forecaster.reset().await;
}
Ok(())
}
/// Fit a model to time series data
#[tauri::command]
pub async fn timeseries_fit(
state: State<'_, AppState>,
data: TimeSeriesData,
config: ForecastConfig,
) -> Result<FitMetrics, String> {
let guard = state.timeseries_forecaster().read().await;
let forecaster = guard.as_ref().ok_or("Time Series Forecaster not initialized")?;
forecaster.fit(data, config).await.map_err(|e| e.to_string())
}
/// Generate forecasts from the fitted model
#[tauri::command]
pub async fn timeseries_forecast(
state: State<'_, AppState>,
) -> Result<ForecastResult, String> {
let guard = state.timeseries_forecaster().read().await;
let forecaster = guard.as_ref().ok_or("Time Series Forecaster not initialized")?;
forecaster.forecast().await.map_err(|e| e.to_string())
}
/// Get forecaster status
#[tauri::command]
pub async fn timeseries_status(state: State<'_, AppState>) -> Result<ForecasterStatus, String> {
let guard = state.timeseries_forecaster().read().await;
let forecaster = guard.as_ref().ok_or("Time Series Forecaster not initialized")?;
Ok(forecaster.status().await)
}
/// Check if forecaster is initialized
#[tauri::command]
pub async fn timeseries_is_initialized(state: State<'_, AppState>) -> Result<bool, String> {
let guard = state.timeseries_forecaster().read().await;
if let Some(forecaster) = guard.as_ref() {
Ok(forecaster.is_initialized().await)
} else {
Ok(false)
}
}
/// Generate sample data for the given dataset type
#[tauri::command]
pub async fn timeseries_generate_sample(
dataset: SampleDataset,
length: usize,
) -> Result<TimeSeriesData, String> {
rtx_timeseries_demo::generate_sample_data(dataset, length).map_err(|e| e.to_string())
}
/// Get available sample datasets
#[tauri::command]
pub fn timeseries_get_sample_datasets() -> Vec<SampleDataset> {
rtx_timeseries_demo::SAMPLE_DATASETS.to_vec()
}
/// Get available model types
#[tauri::command]
pub fn timeseries_get_model_types() -> Vec<ModelType> {
vec![
ModelType::Arima,
ModelType::Sarima,
ModelType::Prophet,
ModelType::ExponentialSmoothing,
ModelType::NeuralProphet,
ModelType::Transformer,
]
}
// =============================================================================
// Portfolio Optimizer Demo Commands
// =============================================================================
use rtx_portfolio_demo::PortfolioOptimizer;
use portfolio_shared::{
EfficientFrontier, OptimizationResult, OptimizerStatus,
PortfolioConfig, PortfolioPreset,
};
/// Initialize the portfolio optimizer
#[tauri::command]
pub async fn portfolio_initialize(state: State<'_, AppState>) -> Result<OptimizerStatus, String> {
let optimizer = PortfolioOptimizer::new();
let status = optimizer.status();
let mut guard = state.portfolio_optimizer().write().await;
*guard = Some(optimizer);
Ok(status)
}
/// Reset the portfolio optimizer
#[tauri::command]
pub async fn portfolio_reset(state: State<'_, AppState>) -> Result<(), String> {
let guard = state.portfolio_optimizer().read().await;
if let Some(optimizer) = guard.as_ref() {
drop(guard);
let mut write_guard = state.portfolio_optimizer().write().await;
if let Some(opt) = write_guard.as_mut() {
opt.reset();
}
}
Ok(())
}
/// Configure portfolio for optimization
#[tauri::command]
pub async fn portfolio_configure(
state: State<'_, AppState>,
config: PortfolioConfig,
) -> Result<OptimizerStatus, String> {
let guard = state.portfolio_optimizer().read().await;
let mut optimizer = guard.as_ref().ok_or("Portfolio Optimizer not initialized")?;
drop(guard);
let mut write_guard = state.portfolio_optimizer().write().await;
let opt = write_guard.as_mut().ok_or("Portfolio Optimizer not initialized")?;
opt.initialize(config).map_err(|e| e.to_string())?;
Ok(opt.status())
}
/// Optimize portfolio
#[tauri::command]
pub async fn portfolio_optimize(
state: State<'_, AppState>,
) -> Result<OptimizationResult, String> {
let guard = state.portfolio_optimizer().read().await;
let optimizer = guard.as_ref().ok_or("Portfolio Optimizer not initialized")?;
drop(guard);
let mut write_guard = state.portfolio_optimizer().write().await;
let opt = write_guard.as_mut().ok_or("Portfolio Optimizer not initialized")?;
opt.optimize().map_err(|e| e.to_string())
}
/// Compute efficient frontier
#[tauri::command]
pub async fn portfolio_efficient_frontier(
state: State<'_, AppState>,
num_points: usize,
) -> Result<EfficientFrontier, String> {
let guard = state.portfolio_optimizer().read().await;
let optimizer = guard.as_ref().ok_or("Portfolio Optimizer not initialized")?;
optimizer.compute_efficient_frontier(num_points).map_err(|e| e.to_string())
}
/// Get optimizer status
#[tauri::command]
pub async fn portfolio_status(state: State<'_, AppState>) -> Result<OptimizerStatus, String> {
let guard = state.portfolio_optimizer().read().await;
let optimizer = guard.as_ref().ok_or("Portfolio Optimizer not initialized")?;
Ok(optimizer.status())
}
/// Check if optimizer is initialized
#[tauri::command]
pub async fn portfolio_is_initialized(state: State<'_, AppState>) -> Result<bool, String> {
let guard = state.portfolio_optimizer().read().await;
Ok(guard.is_some() && guard.as_ref().map(|o| o.status().initialized).unwrap_or(false))
}
/// Generate sample assets for a preset
#[tauri::command]
pub async fn portfolio_generate_sample(
preset: PortfolioPreset,
) -> Result<Vec<portfolio_shared::Asset>, String> {
rtx_portfolio_demo::generate_sample_assets(preset).map_err(|e| e.to_string())
}
/// Get available portfolio presets
#[tauri::command]
pub fn portfolio_get_presets() -> Vec<PortfolioPreset> {
rtx_portfolio_demo::SAMPLE_PRESETS.to_vec()
}
// =============================================================================
// Risk Analyzer Demo Commands
// =============================================================================
use rtx_risk_analyzer::RiskAnalyzer;
use risk_analyzer_shared::{
RiskAnalysisRequest, RiskAnalysisResult, RiskAnalyzerStatus,
};
/// Initialize the risk analyzer
#[tauri::command]
pub async fn risk_analyzer_initialize(state: State<'_, AppState>) -> Result<RiskAnalyzerStatus, String> {
let analyzer = RiskAnalyzer::new();
let status = analyzer.status();
let mut guard = state.risk_analyzer().write().await;
*guard = Some(analyzer);
Ok(status)
}
/// Reset the risk analyzer
#[tauri::command]
pub async fn risk_analyzer_reset(state: State<'_, AppState>) -> Result<(), String> {
let mut guard = state.risk_analyzer().write().await;
if let Some(ref mut analyzer) = *guard {
analyzer.reset();
}
Ok(())
}
/// Perform risk analysis on a portfolio
#[tauri::command]
pub async fn risk_analyzer_analyze(
state: State<'_, AppState>,
request: RiskAnalysisRequest,
) -> Result<RiskAnalysisResult, String> {
let guard = state.risk_analyzer().read().await;
let mut analyzer = guard.as_ref().ok_or("Risk Analyzer not initialized")?;
drop(guard);
let mut write_guard = state.risk_analyzer().write().await;
let analyzer = write_guard.as_mut().ok_or("Risk Analyzer not initialized")?;
analyzer.analyze(request).map_err(|e| e.to_string())
}
/// Get risk analyzer status
#[tauri::command]
pub async fn risk_analyzer_status(state: State<'_, AppState>) -> Result<RiskAnalyzerStatus, String> {
let guard = state.risk_analyzer().read().await;
let analyzer = guard.as_ref().ok_or("Risk Analyzer not initialized")?;
Ok(analyzer.status())
}
/// Check if risk analyzer is initialized
#[tauri::command]
pub async fn risk_analyzer_is_initialized(state: State<'_, AppState>) -> Result<bool, String> {
let guard = state.risk_analyzer().read().await;
Ok(guard.is_some())
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_vessel_params_serialization() {
let params = VesselParams {
length: 0.1,
radius: 0.005,
stenosis_ratio: Some(0.5),
};
let json = serde_json::to_string(&params).unwrap();
let deserialized: VesselParams = serde_json::from_str(&json).unwrap();
assert!((deserialized.length - 0.1).abs() < f64::EPSILON);
assert!((deserialized.radius - 0.005).abs() < f64::EPSILON);
assert_eq!(deserialized.stenosis_ratio, Some(0.5));
}
#[test]
fn test_grid_query_params_serialization() {
let params = GridQueryParams {
nx: 50,
ny: 20,
time: 0.0,
};
let json = serde_json::to_string(&params).unwrap();
let deserialized: GridQueryParams = serde_json::from_str(&json).unwrap();
assert_eq!(deserialized.nx, 50);
assert_eq!(deserialized.ny, 20);
}
#[test]
fn test_status_response_serialization() {
let status = StatusResponse {
initialized: true,
trained: false,
inference_count: 100,
avg_inference_time_ms: 15.5,
uptime_secs: 3600,
};
let json = serde_json::to_string(&status).unwrap();
let deserialized: StatusResponse = serde_json::from_str(&json).unwrap();
assert!(deserialized.initialized);
assert!(!deserialized.trained);
assert_eq!(deserialized.inference_count, 100);
}
}
// =============================================================================
// PINN Benchmark Commands
// =============================================================================
use pinn_benchmark_shared::config::ProblemType;
use pinn_benchmark_shared::ipc::{BenchmarkResult, ComparisonResult};
/// Initialize PINN benchmark with configuration
#[tauri::command]
pub async fn pinn_benchmark_initialize(
config: pinn_benchmark_shared::config::BenchmarkConfig,
state: State<'_, AppState>,
) -> Result<(), String> {
let runner = rtx_pinn_benchmark::BenchmarkRunner::new(config);
let mut benchmark = state.pinn_benchmark().write().await;
*benchmark = Some(runner);
Ok(())
}
/// Start PINN benchmark training
#[tauri::command]
pub async fn pinn_benchmark_start_training(
state: State<'_, AppState>,
) -> Result<BenchmarkResult, String> {
let mut benchmark_guard = state.pinn_benchmark().write().await;
let benchmark = benchmark_guard
.as_mut()
.ok_or_else(|| "Benchmark not initialized".to_string())?;
// Run benchmark (this will block for the duration of training)
let result = benchmark
.run(|_progress| {
// Progress callback - could be enhanced to send events
})
.map_err(|e| format!("Training failed: {e}"))?;
Ok(result)
}
/// Get current PINN benchmark status
#[tauri::command]
pub async fn pinn_benchmark_get_status(
state: State<'_, AppState>,
) -> Result<Option<BenchmarkResult>, String> {
let benchmark = state.pinn_benchmark().read().await;
if let Some(runner) = benchmark.as_ref() {
Ok(runner.result().cloned())
} else {
Ok(None)
}
}
/// Compare PINN with reference solver
#[tauri::command]
pub async fn pinn_benchmark_compare(
reference_time_s: f64,
state: State<'_, AppState>,
) -> Result<ComparisonResult, String> {
let benchmark = state.pinn_benchmark().read().await;
let runner = benchmark
.as_ref()
.ok_or_else(|| "Benchmark not initialized".to_string())?;
runner
.compare_with_reference(reference_time_s)
.map_err(|e| format!("Comparison failed: {e}"))
}
/// Reset PINN benchmark
#[tauri::command]
pub async fn pinn_benchmark_reset(
state: State<'_, AppState>,
) -> Result<(), String> {
let mut benchmark = state.pinn_benchmark().write().await;
*benchmark = None;
Ok(())
}