diff --git a/Cargo.lock b/Cargo.lock index 8167628..fd48ec2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -106,6 +106,7 @@ dependencies = [ "bincode", "clap", "criterion", + "csv", "env_logger", "hound", "inverse_distance_weight", @@ -117,6 +118,7 @@ dependencies = [ "rustfft", "splines", "tempfile", + "thiserror", "wavbrro", ] @@ -316,7 +318,7 @@ dependencies = [ "heck", "proc-macro2", "quote", - "syn 2.0.72", + "syn 2.0.87", ] [[package]] @@ -412,9 +414,9 @@ checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" [[package]] name = "csv" -version = "1.3.0" +version = "1.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac574ff4d437a7b5ad237ef331c17ccca63c46479e5b5453eb8e10bb99a759fe" +checksum = "acdc4883a9c96732e4733212c01447ebd805833b7275a73ca3ee080fd77afdaf" dependencies = [ "csv-core", "itoa", @@ -1080,7 +1082,7 @@ checksum = "e0cd7e117be63d3c3678776753929474f3b04a43a080c744d6b0ae2a8c28e222" dependencies = [ "proc-macro2", "quote", - "syn 2.0.72", + "syn 2.0.87", ] [[package]] @@ -1265,9 +1267,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.72" +version = "2.0.87" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc4b9b9bf2add8093d3f2c0204471e951b2285580335de42f9d2534f3ae7a8af" +checksum = "25aa4ce346d03a6dcd68dd8b4010bcb74e54e62c90c573f394c46eae99aba32d" dependencies = [ "proc-macro2", "quote", @@ -1302,6 +1304,26 @@ dependencies = [ "windows-sys", ] +[[package]] +name = "thiserror" +version = "2.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c006c85c7651b3cf2ada4584faa36773bd07bac24acfb39f3c431b36d7e667aa" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "2.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f077553d607adc1caf65430528a576c757a71ed73944b66ebb58ef2bbd243568" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.87", +] + [[package]] name = "tinytemplate" version = "1.2.1" @@ -1435,7 +1457,7 @@ dependencies = [ "once_cell", "proc-macro2", "quote", - "syn 2.0.72", + "syn 2.0.87", "wasm-bindgen-shared", ] @@ -1457,7 +1479,7 @@ checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.72", + "syn 2.0.87", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -1628,5 +1650,5 @@ checksum = "125139de3f6b9d625c39e2efdd73d41bdac468ccd556556440e322be0e1bbd91" dependencies = [ "proc-macro2", "quote", - "syn 2.0.72", + "syn 2.0.87", ] diff --git a/atsc/src/csv.rs b/atsc/src/csv.rs index 98fcdf6..eb08d9b 100644 --- a/atsc/src/csv.rs +++ b/atsc/src/csv.rs @@ -2,7 +2,7 @@ use std::fs::{File, OpenOptions}; use std::path::Path; #[derive(Debug, thiserror::Error)] -enum Error { +pub enum Error { #[error("Failed to open csv file")] OpenFileFailed, @@ -25,15 +25,15 @@ enum Error { type Result = std::result::Result; #[derive(Debug, PartialEq)] -struct Sample { - timestamp: i64, - value: f64, +pub struct Sample { + pub timestamp: i64, + pub value: f64, } /// read_samples_with_headers reads samples from the given file. /// It expects that timestamps are stored under timestamp_field field header /// and values are stored under value_field. -fn read_samples_with_headers( +pub fn read_samples_with_headers( filepath: &Path, timestamp_field: &str, value_field: &str, @@ -75,9 +75,9 @@ fn read_samples_with_headers( /// read_samples reads samples from the given file. /// It assumes that file contains no headers and /// consists of only a single field with values. -fn read_samples(filepath: &Path) -> Result> { +pub fn read_samples(filepath: &Path) -> Result> { let mut reader = open_csv_reader(filepath, false)?; - + // Collect samples let mut samples = Vec::new(); for record in reader.records() { @@ -93,7 +93,7 @@ fn read_samples(filepath: &Path) -> Result> { value, }); } - + Ok(samples) } diff --git a/atsc/src/lib.rs b/atsc/src/lib.rs index 07db1d9..83b5a3c 100644 --- a/atsc/src/lib.rs +++ b/atsc/src/lib.rs @@ -25,5 +25,5 @@ pub mod frame; pub mod header; pub mod utils; -mod csv; +pub mod csv; pub mod optimizer; diff --git a/atsc/src/main.rs b/atsc/src/main.rs index c698452..414c732 100644 --- a/atsc/src/main.rs +++ b/atsc/src/main.rs @@ -80,12 +80,37 @@ fn process_single_file(mut file_path: PathBuf, arguments: &Args) -> Result<(), B file_path.set_extension("wbro"); WavBrro::to_file_with_data(&file_path, &decompressed_data) } + } else if arguments.csv { + // Read samples from csv and compress it + let samples = if arguments.no_header { + debug!("Reading samples from csv with no header"); + read_samples(&file_path)? + } else { + debug!("Reading samples from csv with headers"); + let headers: Vec<&str> = arguments.fields.split(",").collect(); + // Assuming that header[0] is a time field and header[1] is value field + read_samples_with_headers(&file_path, headers[0], headers[1])? + }; + + let data: Vec = samples.into_iter().map(|sample| sample.value).collect(); + + if arguments.verbose { + println!("Input={:?}", data); + } + + // Compress + let compressed_data = compress_data(&data, arguments); + + // Write + file_path.set_extension("bro"); + std::fs::write(file_path, compressed_data)?; } else { // Read an WavBRRO file and compress it let data = WavBrro::from_file(&file_path)?; if arguments.verbose { println!("Input={:?}", data); } + //compress let compressed_data = compress_data(&data, arguments); @@ -169,6 +194,21 @@ struct Args { /// Verbose output, dumps everysample in the input file (for compression) and in the ouput file (for decompression) #[arg(long, action)] verbose: bool, + + /// Defines user input as a CSV file + #[arg(long, action)] + csv: bool, + + /// Defines if the CSV has no header + #[arg(long, action)] + no_header: bool, + + /// Defines names of fields in CSV file. It should follow this format: + /// --fields=TIME_FIELD_NAME,VALUE_FIELD_NAME + /// It assumes that the one before comma is a name of time field and the one + /// after comma is value field. + #[arg(long)] + fields: String, } #[derive(clap::ValueEnum, Default, Clone, Debug)]