diff --git a/.gitignore b/.gitignore index 3cf3af6..75fa5f8 100644 --- a/.gitignore +++ b/.gitignore @@ -5,3 +5,4 @@ *.aliases *.lvlps *.tdms_index +*.log diff --git a/Cargo.toml b/Cargo.toml index 3c39eba..63f6c6f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -7,7 +7,7 @@ homepage = "https://github.com/WiresmithTech/tedium" repository = "https://github.com/WiresmithTech/tedium" description = "A fast and easy TDMS Library for Rust" keywords = ["labview", "ni", "tdms"] -readme = ".\\README.md" +readme = "README.md" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html diff --git a/src/file/mod.rs b/src/file/mod.rs index 39fd73e..3f950db 100644 --- a/src/file/mod.rs +++ b/src/file/mod.rs @@ -10,7 +10,7 @@ use std::{ }; use crate::meta_data::Segment; -use crate::{error::TdmsError, PropertyPath, PropertyValue}; +use crate::{DataType, error::TdmsError, PropertyPath, PropertyValue}; use crate::{index::Index, ChannelPath}; use crate::{ io::writer::{LittleEndianWriter, TdmsWriter}, @@ -149,6 +149,15 @@ impl TdmsFile { let paths = self.index.paths_starting_with(group.path()); paths.filter_map(|path| ChannelPath::try_from(path).ok()) } + + pub fn get_channel_type(&self, channel: &ChannelPath) -> Option { + let data_type = self.index.channel_type(channel); + if let Some(data_type) = data_type { + Some(*data_type) + } else { + None + } + } } impl TdmsFile { @@ -339,4 +348,22 @@ mod tests { .collect(); assert_eq!(channels.len(), 0); } + + #[test] + fn test_get_channel_type() { + let mut file = new_empty_file(); + + let mut writer = file.writer().unwrap(); + writer + .write_channels( + &[ChannelPath::new("group", "channel")], + &[1.0, 2.0, 3.0, 4.0, 5.0, 6.0], + DataLayout::Interleaved, + ) + .unwrap(); + + drop(writer); + let channel_type = file.get_channel_type(&ChannelPath::new("group", "channel")).unwrap(); + assert_eq!(channel_type, DataType::DoubleFloat); + } } diff --git a/src/index/mod.rs b/src/index/mod.rs index 8b2744d..2938d36 100644 --- a/src/index/mod.rs +++ b/src/index/mod.rs @@ -15,7 +15,7 @@ use crate::error::TdmsError; use crate::meta_data::{ObjectMetaData, RawDataIndex, RawDataMeta}; use crate::paths::{ChannelPath, PropertyPath}; use crate::raw_data::DataBlock; -use crate::PropertyValue; +use crate::{DataType, PropertyValue}; /// A store for a given channel point to the data block with its data and the index within that. #[derive(Debug, Clone, PartialEq, Eq)] @@ -173,6 +173,23 @@ impl Index { pub fn get_data_block(&self, index: usize) -> Option<&DataBlock> { self.data_blocks.get(index) } + + // Get the data type for the given channel. + /// + /// Returns None if the channel does not exist. + pub fn channel_type(&self, path: &ChannelPath) -> Option<&DataType> { + self.objects + .get(path.path()) + .and_then(|object| { + object + .latest_data_format + .as_ref() + .and_then(|format| match format { + DataFormat::RawData(meta) => Some(&meta.data_type), + }) + }) + } + } #[cfg(test)] diff --git a/src/lib.rs b/src/lib.rs index 9038061..dc8bebd 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -11,7 +11,7 @@ mod raw_data; pub use error::TdmsError; pub use file::TdmsFile; pub use file::TdmsFileWriter; -pub use io::data_types::TdmsStorageType; +pub use io::data_types::{TdmsStorageType, DataType}; pub use paths::{ChannelPath, PropertyPath}; pub use properties::PropertyValue; pub use raw_data::DataLayout; diff --git a/tests/defragment.rs b/tests/defragment.rs new file mode 100644 index 0000000..395b589 --- /dev/null +++ b/tests/defragment.rs @@ -0,0 +1,138 @@ +mod common; +use tedium::{ChannelPath, PropertyPath, PropertyValue, TdmsFile, DataType, TdmsFileWriter}; +use std::{ + fs::File, + io::{Read, Seek, SeekFrom, Write}, + path::Path, +}; + + +#[test] +fn test_defragment() { + // get the test file + let mut input_file = common::open_test_file(); + + // create a new tdms file for our defragmented output + let path = std::path::Path::new("tests/test_defragment_output.tdms"); + let mut output_file = TdmsFile::create(path).unwrap(); + + // get the properties of the input file and write them to the properties of the output file + let mut all_properties = input_file.read_all_properties(&PropertyPath::file()).unwrap(); + + let mut writer = output_file.writer().unwrap(); + + // convert all_properties to a `&[(&str, PropertyValue)]` we can pass to the writer.write_properties method + let mut properties: Vec<(&str, PropertyValue)> = Vec::new(); + for (key, value) in all_properties.iter() { + let property = value.clone(); + properties.push((key.as_str(), property.clone())); + } + + writer.write_properties(&PropertyPath::file(), &properties).unwrap(); + + // First, collect all group names + let groups: Vec = input_file.list_groups().into_iter().collect(); + + // print the number of groups + println!("num groups: {:?}", groups.len()); + + // Then iterate over the collected group names + for group_path in groups { + + println!("==== GROUP BEGIN ====="); + + // print the group path + println!("group_path: {:?}", group_path); + + // Collect channel names for each group before mutating `input_file` + let channels: Vec = input_file.list_channels_in_group(&group_path).into_iter().collect(); + + // print the number of channels + println!("num channels: {:?}", channels.len()); + + for channel_path in channels { + + println!("---- CHANNEL BEGIN ----"); + + // print the channel path + println!("channel_path: {:?}", channel_path); + + // #todo: refactor this into a `copy_channel()` function + + let channel_length = input_file.channel_length(&channel_path).unwrap(); + + // print the channel length + println!("channel_length: {:?}", channel_length); + + + // print the channel type + let channel_type = input_file.get_channel_type(&channel_path); + match channel_type { + Some(t) => println!("channel_type: {:?}", t), + None => println!("channel_type: None"), + } + + let channel_type = input_file.get_channel_type(&channel_path); + + match channel_type { + + Some(DataType::SingleFloat) => { + let mut data: Vec = vec![0.0; channel_length as usize]; + input_file.read_channel(&channel_path, &mut data).unwrap(); + writer.write_channels(&[channel_path], &data, tedium::DataLayout::Interleaved).unwrap(); + }, + Some(DataType::DoubleFloat) => { + let mut data: Vec = vec![0.0; channel_length as usize]; + input_file.read_channel(&channel_path, &mut data).unwrap(); + writer.write_channels(&[channel_path], &data, tedium::DataLayout::Interleaved).unwrap(); + }, + Some(DataType::I8) => { + let mut data: Vec = vec![0; channel_length as usize]; + input_file.read_channel(&channel_path, &mut data).unwrap(); + writer.write_channels(&[channel_path], &data, tedium::DataLayout::Interleaved).unwrap(); + }, + Some(DataType::I16) => { + let mut data: Vec = vec![0; channel_length as usize]; + input_file.read_channel(&channel_path, &mut data).unwrap(); + writer.write_channels(&[channel_path], &data, tedium::DataLayout::Interleaved).unwrap(); + }, + Some(DataType::I32) => { + let mut data: Vec = vec![0; channel_length as usize]; + input_file.read_channel(&channel_path, &mut data).unwrap(); + writer.write_channels(&[channel_path], &data, tedium::DataLayout::Interleaved).unwrap(); + }, + Some(DataType::I64) => { + let mut data: Vec = vec![0; channel_length as usize]; + input_file.read_channel(&channel_path, &mut data).unwrap(); + writer.write_channels(&[channel_path], &data, tedium::DataLayout::Interleaved).unwrap(); + }, + Some(DataType::U8) => { + let mut data: Vec = vec![0; channel_length as usize]; + input_file.read_channel(&channel_path, &mut data).unwrap(); + writer.write_channels(&[channel_path], &data, tedium::DataLayout::Interleaved).unwrap(); + }, + Some(DataType::U16) => { + let mut data: Vec = vec![0; channel_length as usize]; + }, + Some(DataType::U32) => { + let mut data: Vec = vec![0; channel_length as usize]; + input_file.read_channel(&channel_path, &mut data).unwrap(); + writer.write_channels(&[channel_path], &data, tedium::DataLayout::Interleaved).unwrap(); + }, + Some(DataType::U64) => { + let mut data: Vec = vec![0; channel_length as usize]; + input_file.read_channel(&channel_path, &mut data).unwrap(); + writer.write_channels(&[channel_path], &data, tedium::DataLayout::Interleaved).unwrap(); + }, + Some(data_type) => println!("Unsupported data type: {}", data_type), + None => println!("None"), + } + + println!("---- CHANNEL END ----"); + + }; + println!("==== GROUP END ====="); + } + + +} diff --git a/tests/test_defragment.tdms b/tests/test_defragment.tdms new file mode 100644 index 0000000..c655d68 Binary files /dev/null and b/tests/test_defragment.tdms differ diff --git a/tests/write_properties.rs b/tests/write_properties.rs index 0dbd38a..8e6b5b2 100644 --- a/tests/write_properties.rs +++ b/tests/write_properties.rs @@ -35,7 +35,7 @@ fn write_properties(file: &mut TdmsFile, path let mut writer = file.writer().unwrap(); writer.write_properties(path, TEST_PROPERTIES).unwrap(); - //this one wont exist as a constant. + //this one won't exist as a constant. writer .write_properties( &path,