use openigtlink_rust::error::{IgtlError, Result};
use openigtlink_rust::io::{ClientBuilder, SyncIgtlClient};
use openigtlink_rust::protocol::message::IgtlMessage;
use openigtlink_rust::protocol::types::{
CapabilityMessage, GetCapabilityMessage, RtsTDataMessage, StartTDataMessage,
StopTDataMessage, TDataMessage,
};
use std::env;
use std::time::Duration;
fn main() {
if let Err(e) = run() {
match e {
IgtlError::Io(ref io_err) if io_err.kind() == std::io::ErrorKind::ConnectionRefused => {
eprintln!("\n[ERROR] Connection refused");
eprintln!("\nPlease start a C++ OpenIGTLink server first:");
eprintln!(" • 3D Slicer: Load OpenIGTLink module and start server");
eprintln!(" • PLUS Toolkit: Run PlusServer with tracking configuration");
eprintln!("\nOr run the companion server example:");
eprintln!(" cargo run --example server\n");
}
_ => eprintln!("[ERROR] {}", e),
}
std::process::exit(1);
}
}
fn run() -> Result<()> {
let server_addr = env::args()
.nth(1)
.unwrap_or_else(|| "127.0.0.1:18944".to_string());
println!("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━");
println!(" OpenIGTLink Query & Streaming Control Example");
println!("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n");
println!("[1] Connecting to server at {}...", server_addr);
let mut client = ClientBuilder::new()
.tcp(&server_addr)
.sync()
.build()?;
println!(" ✓ Connected successfully\n");
println!("[2] Querying server capabilities...");
query_capabilities(&mut client)?;
println!("[3] Starting tracking data stream...");
start_tracking_stream(&mut client)?;
println!("[4] Receiving tracking data stream...");
receive_tracking_data(&mut client)?;
println!("[5] Stopping tracking data stream...");
stop_tracking_stream(&mut client)?;
println!("\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━");
println!("✓ Query and streaming control example completed");
println!("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n");
Ok(())
}
fn query_capabilities(client: &mut SyncIgtlClient) -> Result<()> {
let get_capability = GetCapabilityMessage;
let msg = IgtlMessage::new(get_capability, "QueryClient")?;
client.send(&msg)?;
println!(" → Sent GET_CAPABIL query");
let response: IgtlMessage<CapabilityMessage> = client.receive()?;
println!(" ← Received CAPABILITY response");
println!(" Supported message types ({}):", response.content.types.len());
for (i, msg_type) in response.content.types.iter().enumerate() {
println!(" {}. {}", i + 1, msg_type);
}
println!();
Ok(())
}
fn start_tracking_stream(client: &mut SyncIgtlClient) -> Result<()> {
let start_stream = StartTDataMessage {
resolution: 50, coordinate_name: "RAS".to_string(), };
let msg = IgtlMessage::new(start_stream, "QueryClient")?;
client.send(&msg)?;
println!(" → Sent STT_TDATA (resolution: 50ms, coordinate: RAS)");
let response: IgtlMessage<RtsTDataMessage> = client.receive()?;
println!(" ← Received RTS_TDATA acknowledgment");
match response.content.status {
1 => println!(" ✓ Server ready to send tracking data (status: OK)"),
0 => {
println!(" ✗ Server error (status: ERROR)");
return Err(IgtlError::InvalidHeader(
"Server rejected streaming request".into(),
));
}
code => {
println!(" ? Unknown status code: {}", code);
}
}
println!();
Ok(())
}
fn receive_tracking_data(client: &mut SyncIgtlClient) -> Result<()> {
const MAX_MESSAGES: usize = 10;
println!(" Receiving {} TDATA messages...", MAX_MESSAGES);
println!(" (Press Ctrl+C to stop early)\n");
for i in 1..=MAX_MESSAGES {
let tdata: IgtlMessage<TDataMessage> = client.receive()?;
println!(" [{}/{}] TDATA received:", i, MAX_MESSAGES);
println!(" Device: {}", tdata.header.device_name.as_str()?);
println!(
" Tracking tools: {}",
tdata.content.elements.len()
);
for (j, element) in tdata.content.elements.iter().enumerate() {
let tx = element.matrix[0][3];
let ty = element.matrix[1][3];
let tz = element.matrix[2][3];
println!(
" {}. '{}': position ({:.2}, {:.2}, {:.2}) mm",
j + 1,
element.name,
tx,
ty,
tz
);
}
println!();
std::thread::sleep(Duration::from_millis(50));
}
println!(" ✓ Successfully received {} tracking data messages\n", MAX_MESSAGES);
Ok(())
}
fn stop_tracking_stream(client: &mut SyncIgtlClient) -> Result<()> {
let stop_stream = StopTDataMessage;
let msg = IgtlMessage::new(stop_stream, "QueryClient")?;
client.send(&msg)?;
println!(" → Sent STP_TDATA");
println!(" ✓ Streaming stopped\n");
Ok(())
}