use openigtlink_rust::error::Result;
use openigtlink_rust::io::IgtlClient;
use openigtlink_rust::protocol::message::IgtlMessage;
use openigtlink_rust::protocol::types::{TDataMessage, TrackingDataElement, TrackingInstrumentType};
use std::f32::consts::PI;
use std::thread;
use std::time::{Duration, Instant};
fn main() {
if let Err(e) = run() {
eprintln!("[ERROR] {}", e);
std::process::exit(1);
}
}
fn run() -> Result<()> {
let mut client = IgtlClient::connect("127.0.0.1:18944")?;
println!("[INFO] Connected to OpenIGTLink server\n");
println!("=== Surgical Tool Tracking Simulation ===");
println!("Instruments: Scalpel, Probe, Catheter");
println!("Update Rate: 60 Hz");
println!("Duration: 10 seconds");
println!("Tracking System: Optical (simulated)\n");
simulate_tracking(&mut client)?;
println!("\n[INFO] Tracking simulation completed successfully");
Ok(())
}
fn simulate_tracking(client: &mut IgtlClient) -> Result<()> {
let update_rate: usize = 60; let duration_sec = 10;
let total_frames = update_rate * duration_sec;
let frame_interval = Duration::from_millis(1000 / update_rate as u64);
for frame_num in 0..total_frames {
let start_time = Instant::now();
let time_sec = frame_num as f32 / update_rate as f32;
let mut tdata = TDataMessage::empty();
let scalpel = create_scalpel_tracking(time_sec);
tdata.add_element(scalpel);
let probe = create_probe_tracking(time_sec);
tdata.add_element(probe);
let catheter = create_catheter_tracking(time_sec);
tdata.add_element(catheter);
let msg = IgtlMessage::new(tdata.clone(), "OpticalTracker")?;
client.send(&msg)?;
if frame_num % update_rate == 0 {
let seconds = frame_num / update_rate;
println!("[{}s] Tracking update:", seconds);
for element in &tdata.elements {
let x = element.matrix[0][3];
let y = element.matrix[1][3];
let z = element.matrix[2][3];
println!(
" {:<10} position: ({:7.2}, {:7.2}, {:7.2}) mm",
element.name, x, y, z
);
}
}
let elapsed = start_time.elapsed();
if elapsed < frame_interval {
thread::sleep(frame_interval - elapsed);
}
}
Ok(())
}
fn create_scalpel_tracking(time_sec: f32) -> TrackingDataElement {
let center_x = 100.0;
let center_y = 50.0;
let center_z = 200.0;
let radius = 30.0;
let period = 3.0;
let angle = (time_sec / period) * 2.0 * PI;
let x = center_x + radius * angle.cos();
let y = center_y + radius * angle.sin();
let z = center_z;
let cos_a = angle.cos();
let sin_a = angle.sin();
TrackingDataElement {
name: "Scalpel".to_string(),
instrument_type: TrackingInstrumentType::Instrument6D,
matrix: [
[cos_a, -sin_a, 0.0, x],
[sin_a, cos_a, 0.0, y],
[0.0, 0.0, 1.0, z],
],
}
}
fn create_probe_tracking(time_sec: f32) -> TrackingDataElement {
let center_x = 150.0;
let center_y = 80.0;
let center_z = 180.0;
let amplitude = 20.0;
let frequency = 2.0;
let x = center_x;
let y = center_y;
let z = center_z + amplitude * (time_sec * frequency * 2.0 * PI).sin();
TrackingDataElement::with_translation("Probe", TrackingInstrumentType::Instrument6D, x, y, z)
}
fn create_catheter_tracking(time_sec: f32) -> TrackingDataElement {
let start_x = 80.0;
let start_y = 120.0;
let start_z = 150.0;
let speed = 10.0;
let x = start_x;
let y = start_y;
let z = start_z + speed * time_sec;
let rotation_angle = time_sec * 0.1;
let cos_r = rotation_angle.cos();
let sin_r = rotation_angle.sin();
TrackingDataElement {
name: "Catheter".to_string(),
instrument_type: TrackingInstrumentType::Instrument6D,
matrix: [
[cos_r, -sin_r, 0.0, x],
[sin_r, cos_r, 0.0, y],
[0.0, 0.0, 1.0, z],
],
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_scalpel_tracking() {
let t0 = create_scalpel_tracking(0.0);
let t1 = create_scalpel_tracking(0.75);
let x0 = t0.matrix[0][3];
let y0 = t0.matrix[1][3];
let x1 = t1.matrix[0][3];
let y1 = t1.matrix[1][3];
assert!((x0 - x1).abs() > 10.0 || (y0 - y1).abs() > 10.0);
}
#[test]
fn test_probe_tracking() {
let t0 = create_probe_tracking(0.0);
let t1 = create_probe_tracking(0.125);
let z0 = t0.matrix[2][3];
let z1 = t1.matrix[2][3];
assert!((z0 - z1).abs() > 10.0);
assert_eq!(t0.matrix[0][3], t1.matrix[0][3]);
assert_eq!(t0.matrix[1][3], t1.matrix[1][3]);
}
#[test]
fn test_catheter_tracking() {
let t0 = create_catheter_tracking(0.0);
let t1 = create_catheter_tracking(1.0);
let z0 = t0.matrix[2][3];
let z1 = t1.matrix[2][3];
let expected_delta = 10.0;
assert!((z1 - z0 - expected_delta).abs() < 0.1);
}
#[test]
fn test_tdata_message_creation() {
let mut tdata = TDataMessage::empty();
assert_eq!(tdata.len(), 0);
tdata.add_element(create_scalpel_tracking(0.0));
tdata.add_element(create_probe_tracking(0.0));
tdata.add_element(create_catheter_tracking(0.0));
assert_eq!(tdata.len(), 3);
assert_eq!(tdata.elements[0].name, "Scalpel");
assert_eq!(tdata.elements[1].name, "Probe");
assert_eq!(tdata.elements[2].name, "Catheter");
}
}