openigtlink_rust/protocol/types/
tdata.rs1use crate::error::{IgtlError, Result};
84use crate::protocol::message::Message;
85use bytes::{Buf, BufMut};
86
87#[derive(Debug, Clone, Copy, PartialEq, Eq)]
89#[repr(u8)]
90pub enum TrackingInstrumentType {
91 Tracker = 1,
92 Instrument6D = 2,
93 Instrument3D = 3,
94 Instrument5D = 4,
95}
96
97impl TrackingInstrumentType {
98 fn from_u8(value: u8) -> Result<Self> {
99 match value {
100 1 => Ok(TrackingInstrumentType::Tracker),
101 2 => Ok(TrackingInstrumentType::Instrument6D),
102 3 => Ok(TrackingInstrumentType::Instrument3D),
103 4 => Ok(TrackingInstrumentType::Instrument5D),
104 _ => Err(IgtlError::InvalidHeader(format!(
105 "Invalid tracking instrument type: {}",
106 value
107 ))),
108 }
109 }
110}
111
112#[derive(Debug, Clone, PartialEq)]
114pub struct TrackingDataElement {
115 pub name: String,
117 pub instrument_type: TrackingInstrumentType,
119 pub matrix: [[f32; 4]; 3],
122}
123
124impl TrackingDataElement {
125 pub fn new(
127 name: impl Into<String>,
128 instrument_type: TrackingInstrumentType,
129 matrix: [[f32; 4]; 3],
130 ) -> Self {
131 TrackingDataElement {
132 name: name.into(),
133 instrument_type,
134 matrix,
135 }
136 }
137
138 pub fn identity(name: impl Into<String>, instrument_type: TrackingInstrumentType) -> Self {
140 TrackingDataElement {
141 name: name.into(),
142 instrument_type,
143 matrix: [
144 [1.0, 0.0, 0.0, 0.0],
145 [0.0, 1.0, 0.0, 0.0],
146 [0.0, 0.0, 1.0, 0.0],
147 ],
148 }
149 }
150
151 pub fn with_translation(
153 name: impl Into<String>,
154 instrument_type: TrackingInstrumentType,
155 x: f32,
156 y: f32,
157 z: f32,
158 ) -> Self {
159 TrackingDataElement {
160 name: name.into(),
161 instrument_type,
162 matrix: [[1.0, 0.0, 0.0, x], [0.0, 1.0, 0.0, y], [0.0, 0.0, 1.0, z]],
163 }
164 }
165}
166
167#[derive(Debug, Clone, PartialEq)]
174pub struct TDataMessage {
175 pub elements: Vec<TrackingDataElement>,
177}
178
179impl TDataMessage {
180 pub fn new(elements: Vec<TrackingDataElement>) -> Self {
182 TDataMessage { elements }
183 }
184
185 pub fn empty() -> Self {
187 TDataMessage {
188 elements: Vec::new(),
189 }
190 }
191
192 pub fn add_element(&mut self, element: TrackingDataElement) {
194 self.elements.push(element);
195 }
196
197 pub fn len(&self) -> usize {
199 self.elements.len()
200 }
201
202 pub fn is_empty(&self) -> bool {
204 self.elements.is_empty()
205 }
206}
207
208impl Message for TDataMessage {
209 fn message_type() -> &'static str {
210 "TDATA"
211 }
212
213 fn encode_content(&self) -> Result<Vec<u8>> {
214 let mut buf = Vec::with_capacity(self.elements.len() * 70);
215
216 for element in &self.elements {
217 let mut name_bytes = [0u8; 20];
219 let name_str = element.name.as_bytes();
220 let copy_len = name_str.len().min(19);
221 name_bytes[..copy_len].copy_from_slice(&name_str[..copy_len]);
222 buf.extend_from_slice(&name_bytes);
223
224 buf.put_u8(element.instrument_type as u8);
226
227 buf.put_u8(0);
229
230 for row in &element.matrix {
232 for &val in row {
233 buf.put_f32(val);
234 }
235 }
236 }
237
238 Ok(buf)
239 }
240
241 fn decode_content(mut data: &[u8]) -> Result<Self> {
242 let mut elements = Vec::new();
243
244 while data.len() >= 70 {
245 let name_bytes = &data[..20];
247 data.advance(20);
248
249 let name_len = name_bytes.iter().position(|&b| b == 0).unwrap_or(20);
250 let name = String::from_utf8(name_bytes[..name_len].to_vec())?;
251
252 let instrument_type = TrackingInstrumentType::from_u8(data.get_u8())?;
254
255 let _reserved = data.get_u8();
257
258 let mut matrix = [[0.0f32; 4]; 3];
260 for row in &mut matrix {
261 for val in row {
262 *val = data.get_f32();
263 }
264 }
265
266 elements.push(TrackingDataElement {
267 name,
268 instrument_type,
269 matrix,
270 });
271 }
272
273 if !data.is_empty() {
274 return Err(IgtlError::InvalidSize {
275 expected: 0,
276 actual: data.len(),
277 });
278 }
279
280 Ok(TDataMessage { elements })
281 }
282}
283
284#[cfg(test)]
285mod tests {
286 use super::*;
287
288 #[test]
289 fn test_message_type() {
290 assert_eq!(TDataMessage::message_type(), "TDATA");
291 }
292
293 #[test]
294 fn test_instrument_type() {
295 assert_eq!(TrackingInstrumentType::Tracker as u8, 1);
296 assert_eq!(TrackingInstrumentType::Instrument6D as u8, 2);
297 assert_eq!(TrackingInstrumentType::Instrument3D as u8, 3);
298 assert_eq!(TrackingInstrumentType::Instrument5D as u8, 4);
299 }
300
301 #[test]
302 fn test_empty() {
303 let msg = TDataMessage::empty();
304 assert!(msg.is_empty());
305 assert_eq!(msg.len(), 0);
306 }
307
308 #[test]
309 fn test_identity() {
310 let elem = TrackingDataElement::identity("Tool1", TrackingInstrumentType::Instrument6D);
311 assert_eq!(elem.matrix[0], [1.0, 0.0, 0.0, 0.0]);
312 assert_eq!(elem.matrix[1], [0.0, 1.0, 0.0, 0.0]);
313 assert_eq!(elem.matrix[2], [0.0, 0.0, 1.0, 0.0]);
314 }
315
316 #[test]
317 fn test_with_translation() {
318 let elem = TrackingDataElement::with_translation(
319 "Tool1",
320 TrackingInstrumentType::Tracker,
321 10.0,
322 20.0,
323 30.0,
324 );
325 assert_eq!(elem.matrix[0][3], 10.0);
326 assert_eq!(elem.matrix[1][3], 20.0);
327 assert_eq!(elem.matrix[2][3], 30.0);
328 }
329
330 #[test]
331 fn test_add_element() {
332 let mut msg = TDataMessage::empty();
333 msg.add_element(TrackingDataElement::identity(
334 "Tool1",
335 TrackingInstrumentType::Tracker,
336 ));
337 assert_eq!(msg.len(), 1);
338 }
339
340 #[test]
341 fn test_encode_single_element() {
342 let elem = TrackingDataElement::identity("Test", TrackingInstrumentType::Instrument6D);
343 let msg = TDataMessage::new(vec![elem]);
344 let encoded = msg.encode_content().unwrap();
345
346 assert_eq!(encoded.len(), 70);
347 assert_eq!(encoded[20], 2); assert_eq!(encoded[21], 0);
351 }
352
353 #[test]
354 fn test_roundtrip_single() {
355 let matrix = [
356 [1.0, 0.0, 0.0, 10.0],
357 [0.0, 1.0, 0.0, 20.0],
358 [0.0, 0.0, 1.0, 30.0],
359 ];
360
361 let original = TDataMessage::new(vec![TrackingDataElement::new(
362 "Tracker1",
363 TrackingInstrumentType::Tracker,
364 matrix,
365 )]);
366
367 let encoded = original.encode_content().unwrap();
368 let decoded = TDataMessage::decode_content(&encoded).unwrap();
369
370 assert_eq!(decoded.elements.len(), 1);
371 assert_eq!(decoded.elements[0].name, "Tracker1");
372 assert_eq!(
373 decoded.elements[0].instrument_type,
374 TrackingInstrumentType::Tracker
375 );
376 assert_eq!(decoded.elements[0].matrix, matrix);
377 }
378
379 #[test]
380 fn test_roundtrip_multiple() {
381 let original = TDataMessage::new(vec![
382 TrackingDataElement::identity("Tool1", TrackingInstrumentType::Instrument6D),
383 TrackingDataElement::with_translation(
384 "Tool2",
385 TrackingInstrumentType::Instrument3D,
386 5.0,
387 10.0,
388 15.0,
389 ),
390 ]);
391
392 let encoded = original.encode_content().unwrap();
393 let decoded = TDataMessage::decode_content(&encoded).unwrap();
394
395 assert_eq!(decoded.elements.len(), 2);
396 assert_eq!(decoded.elements[0].name, "Tool1");
397 assert_eq!(decoded.elements[1].name, "Tool2");
398 }
399
400 #[test]
401 fn test_name_truncation() {
402 let long_name = "ThisIsAVeryLongNameThatExceedsTwentyCharacters";
403 let elem = TrackingDataElement::identity(long_name, TrackingInstrumentType::Tracker);
404 let msg = TDataMessage::new(vec![elem]);
405
406 let encoded = msg.encode_content().unwrap();
407 let decoded = TDataMessage::decode_content(&encoded).unwrap();
408
409 assert!(decoded.elements[0].name.len() <= 19);
410 }
411
412 #[test]
413 fn test_empty_message() {
414 let msg = TDataMessage::empty();
415 let encoded = msg.encode_content().unwrap();
416 let decoded = TDataMessage::decode_content(&encoded).unwrap();
417
418 assert_eq!(decoded.elements.len(), 0);
419 assert_eq!(encoded.len(), 0);
420 }
421
422 #[test]
423 fn test_decode_invalid_size() {
424 let data = vec![0u8; 69]; let result = TDataMessage::decode_content(&data);
426 assert!(result.is_err());
427 }
428}