MVN accompanying data parser (from sd directory) done without CLI parser enough

This commit is contained in:
Danila Gamkov 2025-01-28 18:02:38 +03:00
parent 2ace2218b9
commit 2abb435f87
3 changed files with 327 additions and 74 deletions

45
Cargo.lock generated
View File

@ -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",
]

View File

@ -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"

View File

@ -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<TM_data>; 4] = [Vec::new(), Vec::new(), Vec::new(), Vec::new()];
let reader = BufReader::new(data);
let buf_all: Vec<String> = 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<String> = 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::<String>().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<Utc> = DateTime::from_utc(naive_dtime, Utc);
let timestamp = naive_dtime.timestamp();
let temp = TM_data::new(idx.to_string(),
val[1].parse::<f32>().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<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(());
}
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 <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");
}