290 lines
8.7 KiB
Rust

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;
#[derive(Debug)]
struct TMtemp {
// idx: String,
val: f32,
timestamp_s: i64,
date: String,
time: String,
}
impl TMtemp {
pub fn new(val: f32,
timestamp_s: i64,
date: String,
time: String) -> TMtemp {
TMtemp { val, timestamp_s, date, time }
}
}
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<String> = {
let mut err = Vec::new();
err.push("Error parsing file".to_string());
err
};
pub static ref tm_idx: Vec<String> = {
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<TMtemp>; 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<TMtemp>; 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<Vec<String>, String> {
let mut path_vec: Vec<String> = 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<TMtemp>; 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<String> = 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<String> = 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::<String>())
.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::<f32>() {
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<TMtemp>; 4]) {
for elem in data {
elem.sort_by(|a, b| a.timestamp_s.cmp(&b.timestamp_s))
}
}
fn save_data_csv(data: &Vec<TMtemp>, fname: &str, disp_flag: bool) -> Result<(), String> {
let mut data_v: Vec<String> = 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(());
}
}
fn main() {
use crate::mvn_tm_temp::*;
let arguments = Command::new("mvn_accomp_csv")
.version("0.1.0")
.author("Danila Gamkov <danila_gamkov@cosmos.ru>")
.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::<String>("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::<String>("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");
}