diff --git a/Cargo.lock b/Cargo.lock index 1c537f0..3c35d19 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,6 +2,15 @@ # It is not intended for manual editing. version = 4 +[[package]] +name = "aho-corasick" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" +dependencies = [ + "memchr", +] + [[package]] name = "android-tzdata" version = "0.1.1" @@ -207,6 +216,12 @@ version = "0.4.25" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "04cbf5b083de1c7e0222a7a51dbfdba1cbe1c6ab0b15e29fff3f6c077fd9cd9f" +[[package]] +name = "memchr" +version = "2.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" + [[package]] name = "num-traits" version = "0.2.19" @@ -240,6 +255,35 @@ dependencies = [ "proc-macro2", ] +[[package]] +name = "regex" +version = "1.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" + [[package]] name = "rustversion" version = "1.0.19" @@ -274,6 +318,7 @@ dependencies = [ "chrono", "clap", "lazy_static", + "regex", "walkdir", ] diff --git a/Cargo.toml b/Cargo.toml index 2250f95..6af792b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -7,4 +7,6 @@ edition = "2021" chrono = "0.4" clap = "4.*" lazy_static = {version = "1.5.0", features = ["spin_no_std"]} +regex = "1.7.0" walkdir = "2.3.2" + diff --git a/src/main.rs b/src/main.rs index 6844ed2..6a153a5 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,83 +1,289 @@ -use std::fs::File; -use std::io::{BufRead, BufReader}; -use std::collections::HashMap; -use chrono::{NaiveDateTime, DateTime, Utc}; +use clap::{Arg, Command}; +pub mod mvn_tm_temp { + use std::fs::File; + use std::io::{BufRead, BufReader}; + use chrono::{NaiveDateTime}; + use lazy_static::lazy_static; + use regex::Regex; + use walkdir::WalkDir; -pub mod tm_sens_mvn_data { + #[derive(Debug)] + struct TMtemp { + // idx: String, + val: f32, + timestamp_s: i64, + date: String, + time: String, + } -} - -#[derive(Debug)] -struct TM_data { - idx: String, - val: f32, - timestamp_s: i64, - date: String, - time: String, -} - -impl TM_data { - pub fn new(idx: String, - val: f32, - timestamp_s: i64, - date: String, - time: String) -> TM_data { - TM_data { idx, val, timestamp_s, date, time } - } -} - - -fn main() { - let dtime_fmt = "%d.%m.%Y %H:%M:%S%.f"; - - let mut data = File::open("data.txt").unwrap(); - let mut tm_data_v: [Vec; 4] = [Vec::new(), Vec::new(), Vec::new(), Vec::new()]; - - let reader = BufReader::new(data); - - let buf_all: Vec = reader.lines() - .map(|line| line.expect("error")) - .collect(); - - let mut tm_idx = Vec::new(); - tm_idx.push("T1MBH"); - tm_idx.push("T2MBH"); - tm_idx.push("T3MBH"); - tm_idx.push("T4MBH"); - - let mut val: Vec = Vec::new(); - - for elem in buf_all { - for (i, idx) in tm_idx.clone().into_iter().enumerate() { - if elem.contains(idx) - { - val = elem.split_whitespace() - .map(|elem| elem.parse::().unwrap()) - .collect(); - - let dtime_str = format!("{} {}", val[4].clone(), val[5].clone()); - - let naive_dtime = NaiveDateTime::parse_from_str(&dtime_str, dtime_fmt).unwrap(); - - // let utc_dtime: DateTime = DateTime::from_utc(naive_dtime, Utc); - - let timestamp = naive_dtime.timestamp(); - - let temp = TM_data::new(idx.to_string(), - val[1].parse::().unwrap(), - timestamp, - val[4].clone(), - val[5].clone()); - - tm_data_v[i].push(temp); - } + impl TMtemp { + pub fn new(val: f32, + timestamp_s: i64, + date: String, + time: String) -> TMtemp { + TMtemp { val, timestamp_s, date, time } } } - for elem in &mut tm_data_v { - elem.sort_by(|a, b| a.timestamp_s.cmp(&b.timestamp_s)) + lazy_static! { + pub static ref dtime_fmt: String = String::from("%d.%m.%Y %H:%M:%S%.f"); + pub static ref pattern_fname: String = String::from("MVN_.*_A-.*.txt"); + + pub static ref err_patterns: Vec = { + let mut err = Vec::new(); + err.push("Error parsing file".to_string()); + err + }; + + pub static ref tm_idx: Vec = { + let mut tm = Vec::new(); + tm.push("T1MBH".to_string()); + tm.push("T2MBH".to_string()); + tm.push("T3MBH".to_string()); + tm.push("T4MBH".to_string()); + tm + }; + } + + pub fn parse_data_file(filename_full: &str, + save_data: bool, + disp_data: bool, + silent_mode: bool) -> Result<(), String> { + let filename = match filename_full.rfind('/') { + Some(idx) => filename_full[idx+1..filename_full.len()].to_string(), + _ => filename_full.to_string(), + }; + + let mut tm_data_v: [Vec; 4] = + [Vec::new(), Vec::new(), Vec::new(), Vec::new()]; + + read_data(filename_full, &mut tm_data_v)?; + sort_data(&mut tm_data_v); + + if save_data == true { + for (i, &ref elem) in tm_data_v.iter().enumerate() { + let fname = filename.replace(".txt", &format!("_{}{}", tm_idx[i], ".csv")); + save_data_csv(&elem, &fname, disp_data)?; + + if silent_mode == false { + println!("save csv data to file {}", fname); + } + } + } + return Ok(()); + } + + pub fn parse_data_dir(dir: &str, + save_data: bool, + disp_data: bool, + silent_mode: bool) -> Result<(), String> { + let mut tm_data_v: [Vec; 4] = + [Vec::new(), Vec::new(), Vec::new(), Vec::new()]; + + if silent_mode == false { + println!("parsing data from directory: {} ...", dir); + } + let files = find_files_regex(dir, &pattern_fname)?; + + for file in files { + read_data(&file, &mut tm_data_v)?; + } + + sort_data(&mut tm_data_v); + + if save_data == true { + for (i, &ref elem) in tm_data_v.iter().enumerate() { + let fname = format!("MVN_data_{}{}", tm_idx[i], ".csv"); + save_data_csv(&elem, &fname, disp_data)?; + + if silent_mode == false { + println!("save csv data to file {}", fname); + } + } + } + + return Ok(()); + } + + fn find_files_regex(dir: &str, template: &str) -> Result, String> { + let mut path_vec: Vec = Vec::new(); + + let regex = match Regex::new(template) { + Ok(val) => val, + Err(msg) => { + return Err(format!("Error create regex template ({}): {}", + template, msg)); + } + }; + + let data = WalkDir::new(dir) + .into_iter() + .filter_map(|e| e.ok()) + .filter(|e| regex.is_match(e.file_name().to_str().unwrap())) + .map(|e| e.into_path()); + + for elem in data { + path_vec.push(elem.display().to_string()); + } + + if path_vec.len() == 0 { + return Err(format!( + "Error searching files for pattern ({}): files not found in directory {}", + template, dir + )); + + } + + return Ok(path_vec); + } + + fn read_data(filename: &str, data: &mut [Vec; 4]) -> Result<(), String> { + + let file = match File::open(filename) + { + Ok(_file) => _file, + Err(msg) => { return Err(format!("Error opening data file {}: {}", filename, msg)) } + }; + + let reader_buf = BufReader::new(file); + + let data_buf: Vec = match reader_buf.lines() + .map(|line| line) + .collect() { + Ok(_line) => _line, + Err(msg) => { + return Err(format!("Error reading file {}: {}", filename, msg)) + }, + }; + + let mut val: Vec = Vec::new(); + + for (j, elem) in data_buf.iter().enumerate() { + for (i, idx) in tm_idx.clone().into_iter().enumerate() { + if elem.contains(&idx) + { + val = match elem.split_whitespace() + .map(|elem| elem.parse::()) + .collect() { + Ok(_line) => _line, + Err(msg) => { + return Err(format!("{} {} (string: {}): {}", + err_patterns[0], filename, j, msg)) + }, + }; + + let dtime_str = format!("{} {}", val[4].clone(), val[5].clone()); + let naive_dtime = match NaiveDateTime::parse_from_str(&dtime_str, &dtime_fmt) { + Ok(_dtime) => _dtime, + Err(msg) => { + return Err(format!("{} {} (string: {}). Can not decode datetime: {}", + err_patterns[0], filename, j, msg)) + }, + }; + + let timestamp = naive_dtime.and_utc().timestamp(); + + let temperature = match val[1].parse::() { + Ok(_val) => _val, + Err(msg) => { + return Err(format!("{} {} (string: {}). Temperature string can not convert to float: {}", + err_patterns[0], filename, j, msg)) + }, + }; + + let tm_data = TMtemp::new(temperature, timestamp, + val[4].clone(), val[5].clone()); + data[i].push(tm_data); + } + } + } + + return Ok(()); + } + + fn sort_data(data: &mut [Vec; 4]) { + for elem in data { + elem.sort_by(|a, b| a.timestamp_s.cmp(&b.timestamp_s)) + } + } + + fn save_data_csv(data: &Vec, fname: &str, disp_flag: bool) -> Result<(), String> { + + let mut data_v: Vec = Vec::new(); + + for elem in data { + let s = format!("{};{};{}", elem.date, elem.time, elem.val); + if disp_flag == true { + println!("{}", s); + } + data_v.push(s); + } + + match std::fs::write(fname, data_v.join("\n")) { + Ok(f) => f, + Err(msg) => { + return Err(format!("Error writing data to csv file {}: {}", fname, msg)) + } + } + + return Ok(()); } - println!("{:#?}", tm_data_v[0]); +} + + + + +fn main() { + use crate::mvn_tm_temp::*; + + let arguments = Command::new("mvn_accomp_csv") + .version("0.1.0") + .author("Danila Gamkov ") + .about("raw accompanying MVN data parser") + .arg( + Arg::new("file") + .short('f') + .long("filename") + .help(format!("file with accompanying MVN data ({})", pattern_fname.to_string())) + + ) + .arg( + Arg::new("dir") + .short('d') + .long("directory") + .help(format!("directory with accompanying MVN data")) + + ) + .get_matches(); + + match arguments.get_one::("file") { + Some(fname) => { + match parse_data_file(&fname.clone(), true, true, false) { + Ok(elem) => elem, + Err(msg) => { println!("{}", msg); return; } + + } + return; + }, + _ => { } + + }; + + match arguments.get_one::("dir") { + Some(path) => { + match parse_data_dir(path, true, true, false) { + Ok(_) => {} + Err(msg) => { println!("{}", msg); return; } + + } + return; + + }, + _ => { } + + } + + println!("Unexpected command. Type --help for more iformation"); }