290 lines
8.7 KiB
Rust
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");
|
|
}
|