1use crate::data_type::{TDMSValue, TdmsDataType};
2use crate::{to_i32, to_u32, to_u64};
3use crate::{
4 Big, General, InvalidDAQmxDataIndex, InvalidSegment, Little, StringConversionError, TdmsError,
5};
6use indexmap::{indexmap, IndexMap};
7use std::io::{Read, Seek};
8
9const K_TOC_META_DATA: u32 = 1 << 1;
11const K_TOC_NEW_OBJ_LIST: u32 = 1 << 2;
14const K_TOC_RAW_DATA: u32 = 1 << 3;
15const K_TOC_INTERLEAVED_DATA: u32 = 1 << 5;
16const K_TOC_BIG_ENDIAN: u32 = 1 << 6;
17const K_TOC_DAQMX_RAW_DATA: u32 = 1 << 7;
18
19#[derive(Clone, Copy, Debug)]
21pub enum Endianness {
22 Little,
23 Big,
24}
25
26#[derive(Debug, Clone)]
27pub struct Segment {
29 pub lead_in: LeadIn,
30 pub metadata: Option<Metadata>,
31 pub start_pos: u64,
32 pub end_pos: u64,
33 pub groups: IndexMap<GroupPath, Option<IndexMap<ChannelPath, Channel>>>,
34 pub chunk_size: u64,
35}
36
37pub type GroupPath = String;
39pub type ChannelPath = String;
41
42#[derive(Clone, Debug)]
43pub struct Channel {
44 pub full_path: String,
45 pub group_path: String,
46 pub path: String,
47 pub data_type: TdmsDataType,
48 pub raw_data_index: Option<RawDataIndex>,
49 pub daqmx_data_index: Option<DAQmxDataIndex>,
50 pub properties: Vec<MetadataProperty>,
51 pub chunk_positions: Vec<ChannelPositions>,
52 pub string_offset_pos: Option<ChannelPositions>,
53 pub interleaved_offset: u64,
54}
55
56#[derive(Clone, Debug, Copy)]
57pub struct ChannelPositions(pub u64, pub u64);
58
59impl Segment {
60 pub fn new<R: Read + Seek>(
65 r: &mut R,
66 previous_segment: Option<&Segment>,
67 ) -> Result<Self, TdmsError> {
68 let segment_start_pos = r.stream_position()?;
69 let mut lead_in = [0; 28];
70
71 r.read(&mut lead_in[..])?;
72
73 let lead_in = LeadIn::from_bytes(&lead_in)?;
74
75 let segment_end_pos = lead_in.next_segment_offset + 28 + segment_start_pos;
77
78 let endianness = if lead_in.table_of_contents & K_TOC_BIG_ENDIAN != 0 {
79 Big
80 } else {
81 Little
82 };
83
84 let mut metadata: Option<Metadata> = None;
85 if lead_in.table_of_contents & K_TOC_META_DATA != 0 {
86 metadata = Some(Metadata::from_reader(endianness, r)?);
87 }
88
89 let mut groups: IndexMap<GroupPath, Option<IndexMap<ChannelPath, Channel>>> =
94 IndexMap::<GroupPath, Option<IndexMap<ChannelPath, Channel>>>::new();
95
96 let mut data_pos: u64 = segment_start_pos + lead_in.raw_data_offset;
99 let mut interleaved_total_size: u64 = 0;
100 let mut chunk_size: u64 = 0;
101
102 match &mut metadata {
103 Some(metadata) => {
104 for obj in &mut metadata.objects {
105 let path = obj.object_path.clone();
106 let paths: Vec<&str> = path.split("/").collect();
107
108 if obj.object_path == "/" || paths.len() < 3 {
109 continue;
110 }
111 let mut data_type: TdmsDataType = TdmsDataType::Void;
112
113 if previous_segment.is_some()
114 && obj.raw_data_index.is_none()
115 && obj.daqmx_data_index.is_none()
116 {
117 match previous_segment
118 .unwrap()
119 .get_channel(rem_quotes(paths[1]), rem_quotes(paths[2]))
120 {
121 None => {}
122 Some(c) => {
123 obj.raw_data_index = match &c.raw_data_index {
124 None => None,
125 Some(r) => Some(r.clone()),
126 };
127
128 obj.daqmx_data_index = match &c.daqmx_data_index {
129 None => None,
130 Some(d) => Some(d.clone()),
131 }
132 }
133 }
134 }
135
136 match &obj.raw_data_index {
137 None => {}
138 Some(index) => data_type = index.data_type,
139 }
140
141 match &obj.daqmx_data_index {
142 None => {}
143 Some(index) => data_type = index.data_type,
144 }
145
146 interleaved_total_size += TdmsDataType::get_size(data_type) as u64;
148
149 if paths.len() >= 2 && paths[1] != "" {
150 if !groups.contains_key(rem_quotes(paths[1])) {
151 let _ = groups.insert(rem_quotes(paths[1]).to_string(), None);
152 }
153 }
154
155 if paths.len() >= 3 && paths[2] != "" {
156 let map = groups.get_mut(rem_quotes(paths[1]));
157 let mut start_pos = data_pos.clone();
158 let mut end_pos = 0;
159
160 let mut string_offset_pos: Option<ChannelPositions> = None;
161 let raw_data_index = match &obj.raw_data_index {
162 Some(index) => {
163 let type_size = TdmsDataType::get_size(index.data_type);
164
165 if lead_in.table_of_contents & K_TOC_INTERLEAVED_DATA == 0 {
167 if index.data_type == TdmsDataType::String
168 && index.number_of_bytes.is_some()
169 {
170 start_pos = start_pos + index.number_of_values * 4;
171 string_offset_pos = Some(ChannelPositions(data_pos, data_pos + index.number_of_values * 4));
172
173 data_pos = data_pos + index.number_of_bytes.unwrap();
174 end_pos = data_pos.clone();
175 chunk_size += index.number_of_bytes.unwrap();
176 } else {
177 data_pos = data_pos
178 + (type_size as u64
179 * index.array_dimension as u64
180 * index.number_of_values);
181
182 end_pos = data_pos.clone();
183 chunk_size += type_size as u64
184 * index.array_dimension as u64
185 * index.number_of_values
186 }
187 }
188
189 Some(index.clone())
190 }
191 None => None,
192 };
193
194 let daqmx_data_index = match &obj.daqmx_data_index {
195 Some(index) => Some(index.clone()),
196 None => None,
197 };
198
199 let channel = Channel {
200 full_path: obj.object_path.clone(),
201 group_path: rem_quotes(paths[1]).to_string(),
202 path: rem_quotes(paths[2]).to_string(),
203 data_type,
204 raw_data_index,
205 daqmx_data_index,
206 properties: obj.properties.clone(),
207 chunk_positions: vec![ChannelPositions(start_pos, end_pos)],
208 interleaved_offset: 0,
211 string_offset_pos
212 };
213
214 match map {
215 Some(map) => {
216 match map {
217 Some(map) => {
218 map.insert(rem_quotes(paths[2]).to_string(), channel);
219 }
220 None => {
221 groups.insert(
222 rem_quotes(paths[1]).to_string(),
223 Some(indexmap! {rem_quotes(paths[2]).to_string() => channel}),
224 );
225 }
226 }
227 }
228 None => (),
229 }
230 }
231 }
232 }
233 _ => (),
234 }
235
236 if lead_in.table_of_contents & K_TOC_INTERLEAVED_DATA != 0 {
237 for (_, channels) in groups.iter_mut() {
238 match channels {
239 None => continue,
240 Some(channels) => {
241 for (_, channel) in channels.iter_mut() {
242 let size = TdmsDataType::get_size(channel.data_type);
243
244 channel.interleaved_offset = interleaved_total_size - size as u64;
246 match channel.chunk_positions.get_mut(0) {
248 None => (),
249 Some(positions) => {
250 positions.1 = chunk_size - channel.interleaved_offset
251 + interleaved_total_size
252 }
253 }
254
255 interleaved_total_size += size as u64;
256 }
257 }
258 }
259 }
260 }
261
262 for (_, channels) in groups.iter_mut() {
264 match channels {
265 None => continue,
266 Some(channels) => {
267 for (_, channel) in channels.iter_mut() {
268 if channel.data_type == TdmsDataType::DAQmxRawData {
269 continue;
270 }
271
272 let mut i = 0;
273 loop {
274 let ChannelPositions(prev_start, prev_end) =
275 match channel.chunk_positions.get(i) {
276 None => {
277 return Err(General(String::from(
278 "unable to fetch previous chunk positions",
279 )))
280 }
281 Some(p) => p,
282 };
283
284 let new_start = prev_start + chunk_size;
285 let mut new_end = prev_end + chunk_size;
286
287 if new_start > segment_end_pos {
288 break;
289 }
290
291 if new_end > segment_end_pos {
292 new_end = segment_end_pos;
293 }
294
295 channel
296 .chunk_positions
297 .push(ChannelPositions(new_start, new_end));
298 i += 1;
299 }
300 }
301 }
302 }
303 }
304
305 return Ok(Segment {
308 lead_in,
309 metadata,
310 start_pos: segment_start_pos,
311 end_pos: segment_end_pos,
313 groups,
314 chunk_size,
315 });
316 }
317
318 pub fn endianess(&self) -> Endianness {
320 return if self.lead_in.table_of_contents & K_TOC_BIG_ENDIAN != 0 {
321 Big
322 } else {
323 Little
324 };
325 }
326
327 pub fn has_interleaved_data(&self) -> bool {
329 return self.lead_in.table_of_contents & K_TOC_INTERLEAVED_DATA != 0;
330 }
331
332 pub fn has_daqmx_raw_data(&self) -> bool {
334 return self.lead_in.table_of_contents & K_TOC_DAQMX_RAW_DATA != 0;
335 }
336
337 pub fn has_raw_data(&self) -> bool {
339 return self.lead_in.table_of_contents & K_TOC_RAW_DATA != 0;
340 }
341
342 pub fn has_new_obj_list(&self) -> bool {
344 return self.lead_in.table_of_contents & K_TOC_NEW_OBJ_LIST != 0;
345 }
346
347 pub fn get_channel_mut(&mut self, group_path: &str, path: &str) -> Option<&mut Channel> {
348 let group = match self.groups.get_mut(group_path) {
349 None => return None,
350 Some(g) => g,
351 };
352
353 let channels = match group {
354 None => return None,
355 Some(c) => c,
356 };
357
358 return channels.get_mut(path);
359 }
360
361 pub fn get_channel(&self, group_path: &str, path: &str) -> Option<&Channel> {
362 let group = match self.groups.get(group_path) {
363 None => return None,
364 Some(g) => g,
365 };
366
367 let channels = match group {
368 None => return None,
369 Some(c) => c,
370 };
371
372 return channels.get(path);
373 }
374}
375
376#[derive(Debug, Clone)]
377pub struct LeadIn {
379 pub tag: [u8; 4],
380 pub table_of_contents: u32,
381 pub version_number: u32,
382 pub next_segment_offset: u64,
383 pub raw_data_offset: u64,
384}
385
386impl LeadIn {
387 pub fn from_bytes(lead_in: &[u8; 28]) -> Result<Self, TdmsError> {
390 let mut tag: [u8; 4] = [0; 4];
391 tag.clone_from_slice(&lead_in[0..4]);
392
393 if hex::encode(tag) != String::from("5444536d") {
394 return Err(InvalidSegment());
395 }
396
397 let mut toc: [u8; 4] = [0; 4];
398 toc.clone_from_slice(&lead_in[4..8]);
399
400 let table_of_contents = u32::from_le_bytes(toc);
403
404 let mut version: [u8; 4] = [0; 4];
405 version.clone_from_slice(&lead_in[8..12]);
406
407 let version_number = if table_of_contents & K_TOC_BIG_ENDIAN != 0 {
408 u32::from_be_bytes(version)
409 } else {
410 u32::from_le_bytes(version)
411 };
412
413 let mut offset: [u8; 8] = [0; 8];
414 offset.clone_from_slice(&lead_in[12..20]);
415
416 let next_segment_offset = if table_of_contents & K_TOC_BIG_ENDIAN != 0 {
417 u64::from_be_bytes(offset)
418 } else {
419 u64::from_le_bytes(offset)
420 };
421
422 let mut raw_offset: [u8; 8] = [0; 8];
423 raw_offset.clone_from_slice(&lead_in[20..28]);
424
425 let raw_data_offset = if table_of_contents & K_TOC_BIG_ENDIAN != 0 {
426 u64::from_be_bytes(raw_offset) + 28
427 } else {
428 u64::from_le_bytes(raw_offset) + 28
429 };
430
431 return Ok(LeadIn {
432 tag,
433 table_of_contents,
434 version_number,
435 next_segment_offset,
436 raw_data_offset,
437 });
438 }
439}
440
441#[derive(Debug, Clone)]
442pub struct Metadata {
445 pub number_of_objects: u32,
446 pub objects: Vec<MetadataObject>,
447}
448
449#[derive(Debug, Clone)]
450pub struct MetadataObject {
453 pub object_path: String,
454 pub raw_data_index: Option<RawDataIndex>,
455 pub daqmx_data_index: Option<DAQmxDataIndex>,
456 pub properties: Vec<MetadataProperty>,
457}
458
459impl Metadata {
460 pub fn from_reader<R: Read + Seek>(
464 endianness: Endianness,
465 r: &mut R,
466 ) -> Result<Self, TdmsError> {
467 let mut buf: [u8; 4] = [0; 4];
468 r.read(&mut buf)?;
469
470 let number_of_objects = match endianness {
471 Little => u32::from_le_bytes(buf),
472 Big => u32::from_be_bytes(buf),
473 };
474
475 let mut objects: Vec<MetadataObject> = vec![];
476
477 for _ in 0..number_of_objects {
478 let mut buf: [u8; 4] = [0; 4];
479 r.read(&mut buf)?;
480
481 let length: u32 = to_u32!(buf, endianness);
482
483 let length = match usize::try_from(length) {
485 Ok(l) => l,
486 Err(_) => {
487 return Err(General(String::from(
488 "error converting strength length to system size",
489 )))
490 }
491 };
492
493 let mut path = vec![0; length];
494 r.read_exact(&mut path)?;
495
496 let object_path = match String::from_utf8(path) {
499 Ok(n) => n,
500 Err(_) => {
501 return Err(StringConversionError(String::from(
502 "unable to convert object path",
503 )))
504 }
505 };
506
507 let mut buf: [u8; 4] = [0; 4];
508 r.read_exact(&mut buf)?;
509 let mut raw_data_index: Option<RawDataIndex> = None;
510 let mut daqmx_data_index: Option<DAQmxDataIndex> = None;
511
512 let first_byte: u32 = to_u32!(buf, endianness);
513
514 if first_byte == 0x69120000 || first_byte == 0x00001269 {
516 let index = DAQmxDataIndex::from_reader(endianness, r, true)?;
517 daqmx_data_index = Some(index);
518 } else if first_byte == 0x69130000
520 || first_byte == 0x0000126A
521 || first_byte == 0x00001369
522 {
523 let index = DAQmxDataIndex::from_reader(endianness, r, false)?;
524 daqmx_data_index = Some(index);
525 } else {
526 if first_byte != 0xFFFFFFFF && first_byte != 0x0000000 {
527 raw_data_index = Some(RawDataIndex::from_reader(endianness, r)?)
528 }
529 }
530
531 r.read_exact(&mut buf)?;
532 let num_of_properties: u32 = to_u32!(buf, endianness);
533
534 let mut properties: Vec<MetadataProperty> = vec![];
536 for _ in 0..num_of_properties {
537 match MetadataProperty::from_reader(endianness, r) {
538 Ok(p) => properties.push(p),
539 Err(e) => return Err(e),
540 };
541 }
542
543 objects.push(MetadataObject {
544 object_path,
545 raw_data_index,
546 daqmx_data_index,
547 properties,
548 });
549 }
550
551 return Ok(Metadata {
552 number_of_objects,
553 objects,
554 });
555 }
556}
557
558#[derive(Debug, Clone)]
559pub struct RawDataIndex {
560 pub data_type: TdmsDataType,
561 pub array_dimension: u32, pub number_of_values: u64,
563 pub number_of_bytes: Option<u64>, }
565
566impl RawDataIndex {
567 pub fn from_reader<R: Read + Seek>(
568 endianness: Endianness,
569 r: &mut R,
570 ) -> Result<Self, TdmsError> {
571 let mut buf: [u8; 4] = [0; 4];
572
573 r.read_exact(&mut buf)?;
575 let data_type = to_i32!(buf, endianness);
576
577 let data_type = TdmsDataType::try_from(data_type)?;
578
579 r.read_exact(&mut buf)?;
580 let array_dimension: u32 = to_u32!(buf, endianness);
581
582 let mut buf: [u8; 8] = [0; 8];
583 r.read_exact(&mut buf)?;
584 let number_of_values = to_u64!(buf, endianness);
585
586 let number_of_bytes: Option<u64> = match data_type {
587 TdmsDataType::String => {
588 r.read_exact(&mut buf)?;
589 let num = to_u64!(buf, endianness);
590
591 Some(num)
592 }
593 _ => None,
594 };
595
596 return Ok(RawDataIndex {
597 data_type,
598 array_dimension,
599 number_of_values,
600 number_of_bytes,
601 });
602 }
603}
604
605#[derive(Debug, Clone)]
606pub struct DAQmxDataIndex {
607 pub data_type: TdmsDataType,
608 pub array_dimension: u32, pub number_of_values: u64,
610 pub format_changing_size: Option<u32>,
611 pub format_changing_vec: Option<Vec<FormatChangingScaler>>,
612 pub buffer_vec_size: u32,
613 pub buffers: Vec<u32>,
614}
615
616impl DAQmxDataIndex {
617 pub fn from_reader<R: Read + Seek>(
618 endianness: Endianness,
619 r: &mut R,
620 is_format_changing: bool,
621 ) -> Result<Self, TdmsError> {
622 let mut buf: [u8; 4] = [0; 4];
623 r.read_exact(&mut buf)?;
624
625 let data_type = to_u32!(buf, endianness);
626
627 if data_type != 0xFFFFFFFF {
628 return Err(InvalidDAQmxDataIndex());
629 }
630
631 r.read_exact(&mut buf)?;
632 let array_dimension = to_u32!(buf, endianness);
633
634 let mut buf: [u8; 8] = [0; 8];
635 r.read_exact(&mut buf)?;
636 let number_of_values = to_u64!(buf, endianness);
637
638 let mut buf: [u8; 4] = [0; 4];
639
640 let format_changing_size: Option<u32> = None;
641 let format_changing_vec: Option<Vec<FormatChangingScaler>> = None;
642 if is_format_changing {
643 r.read_exact(&mut buf)?;
644 let changing_vec_size = to_u32!(buf, endianness);
645
646 let mut vec: Vec<FormatChangingScaler> = vec![];
647 for _ in 0..changing_vec_size {
648 vec.push(FormatChangingScaler::from_reader(endianness, r)?)
649 }
650 }
651
652 r.read_exact(&mut buf)?;
653 let buffer_vec_size = to_u32!(buf, endianness);
654
655 let mut buffers: Vec<u32> = vec![];
656
657 for _ in 0..buffer_vec_size {
658 r.read_exact(&mut buf)?;
659 let elements = to_u32!(buf, endianness);
660
661 buffers.push(elements);
662 }
663
664 return Ok(DAQmxDataIndex {
665 data_type: TdmsDataType::DAQmxRawData,
666 array_dimension,
667 number_of_values,
668 format_changing_size,
669 format_changing_vec,
670 buffer_vec_size,
671 buffers,
672 });
673 }
674}
675
676#[derive(Debug, Clone)]
677pub struct FormatChangingScaler {
678 pub data_type: TdmsDataType,
679 pub raw_buffer_index: u32,
680 pub raw_byte_offset: u32,
681 pub sample_format_bitmap: u32,
682 pub scale_id: u32,
683}
684
685impl FormatChangingScaler {
686 pub fn from_reader<R: Read + Seek>(
687 endianness: Endianness,
688 r: &mut R,
689 ) -> Result<Self, TdmsError> {
690 let mut buf: [u8; 4] = [0; 4];
691 r.read_exact(&mut buf)?;
692
693 let data_type = to_i32!(buf, endianness);
694
695 let data_type = TdmsDataType::try_from(data_type)?;
696
697 r.read_exact(&mut buf)?;
698 let raw_buffer_index = to_u32!(buf, endianness);
699
700 r.read_exact(&mut buf)?;
701 let raw_byte_offset = to_u32!(buf, endianness);
702
703 r.read_exact(&mut buf)?;
704 let sample_format_bitmap = to_u32!(buf, endianness);
705
706 r.read_exact(&mut buf)?;
707 let scale_id = to_u32!(buf, endianness);
708
709 return Ok(FormatChangingScaler {
710 data_type,
711 raw_buffer_index,
712 raw_byte_offset,
713 sample_format_bitmap,
714 scale_id,
715 });
716 }
717}
718
719#[derive(Debug, Clone)]
720pub struct MetadataProperty {
722 pub name: String,
723 pub data_type: TdmsDataType,
724 pub value: TDMSValue,
725}
726
727impl MetadataProperty {
728 pub fn from_reader<R: Read + Seek>(
732 endianness: Endianness,
733 r: &mut R,
734 ) -> Result<Self, TdmsError> {
735 let mut buf: [u8; 4] = [0; 4];
736 r.read_exact(&mut buf)?;
737
738 let length: u32 = to_u32!(buf, endianness);
739
740 let length = match usize::try_from(length) {
742 Ok(l) => l,
743 Err(_) => {
744 return Err(General(String::from(
745 "error converting strength length to system size",
746 )))
747 }
748 };
749
750 let mut name = vec![0; length];
751 r.read_exact(&mut name)?;
752
753 let name = match String::from_utf8(name) {
756 Ok(n) => n,
757 Err(_) => {
758 return Err(StringConversionError(String::from(
759 "unable to convert metadata property name",
760 )))
761 }
762 };
763
764 r.read_exact(&mut buf)?;
766 let data_type = to_i32!(buf, endianness);
767
768 let data_type = TdmsDataType::try_from(data_type)?;
769 let value = TDMSValue::from_reader(endianness, data_type, r)?;
770
771 return Ok(MetadataProperty {
772 name,
773 data_type,
774 value,
775 });
776 }
777}
778
779fn rem_quotes(value: &str) -> &str {
780 let mut original = value.chars();
781 let mut chars = value.clone().chars().peekable();
782
783 match chars.peek() {
784 None => (),
785 Some(first) => {
786 if first.to_string() == "'" {
787 original.next();
788 }
789 }
790 }
791
792 let mut reversed = chars.rev().peekable();
793 match reversed.peek() {
794 None => (),
795 Some(last) => {
796 if last.to_string() == "'" {
797 original.next_back();
798 }
799 }
800 }
801
802 original.as_str()
803}
804
805#[macro_export]
806macro_rules! to_u32 {
807 ( $x:ident, $t:ident ) => {
808 match $t {
809 Little => u32::from_le_bytes($x),
810 Big => u32::from_be_bytes($x),
811 }
812 };
813}
814
815#[macro_export]
816macro_rules! to_i32 {
817 ( $x:ident, $t:ident ) => {
818 match $t {
819 Little => i32::from_le_bytes($x),
820 Big => i32::from_be_bytes($x),
821 }
822 };
823}
824
825#[macro_export]
826macro_rules! to_u64 {
827 ( $x:ident, $t:ident ) => {
828 match $t {
829 Little => u64::from_le_bytes($x),
830 Big => u64::from_be_bytes($x),
831 }
832 };
833}
834
835#[macro_export]
836macro_rules! to_f64 {
837 ( $x:ident, $t:ident ) => {
838 match $t {
839 Little => f64::from_le_bytes($x),
840 Big => f64::from_be_bytes($x),
841 }
842 };
843}