From b44fc029fc2bdee3c8626cb0a9fc4326ec6710ea Mon Sep 17 00:00:00 2001 From: Danila Gamkov Date: Wed, 29 Jan 2025 13:47:36 +0300 Subject: [PATCH] Refactor: 1. add print into console data, 2. rewrite CLI parsing, 4. the output format csv has been reworked. 5. Add information to README. For addition create python code for plot csv data --- .gitignore | 5 ++ Cargo.lock | 13 +++ Cargo.toml | 4 +- README.markdown | 11 ++- asotr_all_unzip_auto.sh | 16 ++++ data/plot_flight_all.py | 122 +++++++++++++++++++++++++++ data/prepare_csv.sh | 30 +++++++ data/trash_prepare_csv.sh | 30 +++++++ src/.vimrc | 132 +++++++++++++++++++++++++++++ src/main.rs | 169 ++++++++++++++++++++++---------------- 10 files changed, 458 insertions(+), 74 deletions(-) create mode 100755 asotr_all_unzip_auto.sh create mode 100644 data/plot_flight_all.py create mode 100755 data/prepare_csv.sh create mode 100755 data/trash_prepare_csv.sh create mode 100644 src/.vimrc diff --git a/.gitignore b/.gitignore index bb657df..6c84d6f 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,7 @@ /target *.asotr* +*.csv +*.png +*.swp +*.zip +asotr_csv diff --git a/Cargo.lock b/Cargo.lock index 7e43bc9..1078afd 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -143,6 +143,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "769b0145982b4b48713e01ec42d61614425f27b7058bda7180a3a41f30104796" dependencies = [ "clap_builder", + "clap_derive", ] [[package]] @@ -157,6 +158,18 @@ dependencies = [ "strsim", ] +[[package]] +name = "clap_derive" +version = "4.5.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "54b755194d6389280185988721fffba69495eed5ee9feeee9a599b53db80318c" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "clap_lex" version = "0.7.4" diff --git a/Cargo.toml b/Cargo.toml index 237e18b..ada95f0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -2,12 +2,14 @@ name = "asotr_csv" version = "0.1.0" edition = "2021" +authors = ["Danila Gamkov /target/release/ csv data are ready to use in directory: \/target/release/ +### Plot csv data in Python +If you want to plot csv data you might use \/**asotr_all_unzip_auto.sh** shell script or use \/data/**plot_flight_all.py** python-script directly for set different configurations. + ## Output asotr_csv data files description **description:** asotr01_data_T.csv - ASOTR1 temperature data in channels 1-6 (in Celsius) @@ -92,10 +96,9 @@ asotr02_data_P.csv - ASOTR2 power data in channels 1-6 (in %) asotr02_data_TSET.csv - ASOTR2 temperature sets in channels 1-6 (in Celsius) **file data csv fromat:** -column 1: Unix timestamp -column 2: date -column 3: time -columns 4-9 - data from control channels (power, temperature or temperature set) +column 1: Unix timestamp in seconds +column 2: timestamp (data and time) +columns 3-8 - data from control channels (power, temperature or temperature set) ## Contatcs diff --git a/asotr_all_unzip_auto.sh b/asotr_all_unzip_auto.sh new file mode 100755 index 0000000..21a001d --- /dev/null +++ b/asotr_all_unzip_auto.sh @@ -0,0 +1,16 @@ +#! /bin/bash + +if [ $# != 1 ] +then + echo "erorr use $0. Right use this script: " + echo "$0 path" +else + cp ./target/release/asotr_csv ./data + path_=$1 + find ${path_} -maxdepth 1 -type d | xargs -I {} ./asotr_unzip.sh {} + + cd ./data + ./asotr_csv -d ${path_} + + python3 ./plot_flight_all.py +fi diff --git a/data/plot_flight_all.py b/data/plot_flight_all.py new file mode 100644 index 0000000..2176b0b --- /dev/null +++ b/data/plot_flight_all.py @@ -0,0 +1,122 @@ +import matplotlib.pyplot as plt +from matplotlib import dates +import pandas as pd +from datetime import datetime +import sys + +font = 10 +print_width = 20 +print_height = 12 +width = 1 +plot_windows = 2 +channels = [1, 1, 1, 1, 1, 1] + +xborders=False +begin=0; +end=0; + +path = '/home/danila/Danila/work/MVN/Soft/asotr_csv/data/' +fname = 'asotr01_data_T.csv' +fname_pow = 'asotr01_data_P.csv' +pict_name = path + "ASOTR1_flight_T_P_all" +ox_dtime_format = '%d.%m.%Y %H:%M' + +legend=['ch1', 'ch2', 'ch3', 'ch4', 'ch5', 'ch6', 'ch7'] +width=[1, 1, 1, 1, 1, 1] + +marker = ['-', '-', '-', '-', '-', '-']; +width_arr = [1, 0.5, 0.2, 0.1, 1, 1] + +fname = [path + fname, path + fname_pow] + +dateparse = lambda x: datetime.strptime(x, "%d.%m.%Y %H:%M:%S.%f") + +data = [pd.read_csv(fname[0], sep=';', parse_dates=['timestamp'], date_parser=dateparse), + pd.read_csv(fname[1], sep=';', parse_dates=['timestamp'], date_parser=dateparse)] + +ch= [[], [], [], [], [], []] +ch_signs = ["temp", "pow"] +data_dict = {"temp": ch, "pow": ch, "time": []} +data_dict["time"] = data[0]['timestamp'] +col=['ch1', 'ch2', 'ch3', 'ch4', 'ch5', 'ch6', 'ch7'] + +for j in range(2): + for index, row, in data[j].iterrows(): + for i in range(6): + ch[i].append(float(row[col[i]])) + data_dict[ch_signs[j]] = ch + ch= [[], [], [], [], [], []] + +len_data = [len(data_dict['temp'][0]), len(data_dict['pow'][0])] +len_ = min(len_data) + +if xborders == False: + begin = 0 + end = len_ - 1 + +if plot_windows == 1: + fig, ax = plt.subplots(figsize=(print_width, print_height), dpi=200) + + i = 0 + for elem in data_dict['temp']: + if channels[i] == 1: + plt.plot(data_dict['time'][begin:end], elem[begin:end], marker[i], linewidth=width[i], label=legend[i]) + i += 1 + + # ax.axvline(x = data['timestamp'][300], color='r', linestyle='-.', label="power=100") + # ax.axvline(x = data['timestamp'][1500], color='b', linestyle='dotted', label="power=0") + ax.tick_params(axis="both", width=1, labelsize=font) + ax.grid(visible=True, linestyle = 'dotted') + ax.set_ylabel('Температура, $^\circ$C', fontsize=font) + ax.set_xlabel('Время', fontsize=font) + ax.legend(fontsize=font) + + date_formatter = dates.DateFormatter(ox_dtime_format) + ax.xaxis.set_major_formatter(date_formatter) + + plt.tight_layout() + fig.savefig(pict_name) + plt.show() + +elif plot_windows == 2: + + fig = plt.figure(figsize=(print_width, print_height), dpi=200) + ax1 = fig.add_subplot(2, 1, 1) + ax2 = fig.add_subplot(2, 1, 2, sharex=ax1) + + i = 0 + for elem in data_dict['temp']: + if channels[i] == 1: + ax1.plot(data_dict['time'][begin:end], elem[begin:end], marker[i], linewidth=width[i], label=legend[i]) + i += 1 + + i = 0 + for elem in data_dict['pow']: + if channels[i] == 1: + ax2.plot(data_dict['time'][begin:end], elem[begin:end], marker[i], linewidth=width[i], label=legend[i]) + i += 1 + + # ax1.axvline(x = data['timestamp'][300], color='r', linestyle='-.', label="power=100") + # ax.axvline(x = data['timestamp'][1500], color='b', linestyle='dotted', label="power=0") + ax1.tick_params(axis="both", width=1, labelsize=font) + ax1.grid(visible=True, linestyle = 'dotted') + ax1.set_ylabel('Температура, $^\circ$C', fontsize=font) + ax1.set_xlabel('Время', fontsize=font) + ax1.legend(fontsize=font) + + date_formatter = dates.DateFormatter('%d.%m.%Y %H') + ax1.xaxis.set_major_formatter(date_formatter) + + ax2.tick_params(axis="both", width=1, labelsize=font) + ax2.grid(visible=True, linestyle = 'dotted') + ax2.set_ylabel('Мощность, %', fontsize=font) + ax2.set_xlabel('Время', fontsize=font) + ax2.legend(fontsize=font) + + date_formatter = dates.DateFormatter(ox_dtime_format) + ax2.xaxis.set_major_formatter(date_formatter) + + plt.tight_layout() + fig.savefig(pict_name) + plt.show() + diff --git a/data/prepare_csv.sh b/data/prepare_csv.sh new file mode 100755 index 0000000..dd92353 --- /dev/null +++ b/data/prepare_csv.sh @@ -0,0 +1,30 @@ +#! /bin/bash + +if [ $# != 2 ] +then + echo "error use $0. Right use this script: " + echo "$0 path_to_file data_type (flight or KDI)" + echo "example 1: $0 ./data/flight/30_12_2024/ASOTR_1_SOTR_T flight" +else + data_file=$1 + data_type=$2 + + if [ "$data_type" == "flight" ] + then + cat ${data_file}.csv | grep -Eo '[0-9]{2}\.[0-9]{2}\.[0-9]{4}' > file1 + cat ${data_file}.csv | grep -Eo [0-9]{2}:.* > file2 + + elif [ "$data_type" == "KDI" ] + then + cat ${data_file}.csv | grep -Eo [0-9]{2}.[0-9]{2}.[0-9]{4} > file1 + cat ${data_file}.csv | grep -Eo [0-9]{2}:.* > file2 + else + echo "error argument of data_type: write \"flight\" or \"KDI\" in second argument" + exit 1 + fi + + paste --delimiter=' ' file1 file2 > file.csv + echo "timestamp;ch1;ch2;ch3;ch4;ch5;ch6" > ${data_file}_clear.csv + cat file.csv >> ${data_file}_clear.csv + rm file1 file2 file.csv +fi diff --git a/data/trash_prepare_csv.sh b/data/trash_prepare_csv.sh new file mode 100755 index 0000000..dd92353 --- /dev/null +++ b/data/trash_prepare_csv.sh @@ -0,0 +1,30 @@ +#! /bin/bash + +if [ $# != 2 ] +then + echo "error use $0. Right use this script: " + echo "$0 path_to_file data_type (flight or KDI)" + echo "example 1: $0 ./data/flight/30_12_2024/ASOTR_1_SOTR_T flight" +else + data_file=$1 + data_type=$2 + + if [ "$data_type" == "flight" ] + then + cat ${data_file}.csv | grep -Eo '[0-9]{2}\.[0-9]{2}\.[0-9]{4}' > file1 + cat ${data_file}.csv | grep -Eo [0-9]{2}:.* > file2 + + elif [ "$data_type" == "KDI" ] + then + cat ${data_file}.csv | grep -Eo [0-9]{2}.[0-9]{2}.[0-9]{4} > file1 + cat ${data_file}.csv | grep -Eo [0-9]{2}:.* > file2 + else + echo "error argument of data_type: write \"flight\" or \"KDI\" in second argument" + exit 1 + fi + + paste --delimiter=' ' file1 file2 > file.csv + echo "timestamp;ch1;ch2;ch3;ch4;ch5;ch6" > ${data_file}_clear.csv + cat file.csv >> ${data_file}_clear.csv + rm file1 file2 file.csv +fi diff --git a/src/.vimrc b/src/.vimrc new file mode 100644 index 0000000..adf6810 --- /dev/null +++ b/src/.vimrc @@ -0,0 +1,132 @@ +set tabstop=4 +set softtabstop=4 +set shiftwidth=4 +set noexpandtab +set colorcolumn=90 +highlight ColorColumnt ctermbg=darkgray +augroup project + autocmd! + autocmd BufRead,BufNewFile *.h,*.c set filetype=c.doxygen +augroup END +let &path.="src/include, src/source," + +" Включаем использование системного буфера +set clipboard=unnamedplus + +" Работа с текстом + +" Python использует 4 пробела для отступов +autocmd FileType python setlocal tabstop=4 shiftwidth=4 + +" Кодировка текста +set encoding=utf-8 +set fileencoding=utf-8 +set fileencodings=utf-8,cp1251,koi8-r,cp866 + +" Поиск по тексту +set hlsearch " подсвечивать результаты поиска + +" Перемещение по тексту +" Когда достигаем границ строки, то перемещаемся на предыдующую/следующую +set whichwrap+=h,l,<,>,[,] + +set number + +" Настройки автодополнения +set completeopt=menu,menuone,noselect + +" Разделение экрана +set splitbelow " разбивать вниз +set splitright " разбивать вправо + + + +" сочетание клавиш + +" Использование h, j, k, l для перемещения с зажатым Ctrl в режиме +" редактирования +inoremap +inoremap +inoremap +inoremap + +let g:mapleader = "\" + +" Переключение между вкладками +nnoremap t :tabnext +nnoremap T :tabprevious + +" Список вкладок +nnoremap tl :tabs + +" nnoremap tn :tabnew +nnoremap tc :tabclose +nnoremap to :tabonly +nnoremap tm :tabmove + +" Редактировать файл в новой вкладке +nnoremap te :tabedit | + +" Выбор вкладки +nnoremap 1 1gt +nnoremap 2 2gt +nnoremap 3 3gt +nnoremap 4 4gt +nnoremap 5 5gt +nnoremap 6 6gt +nnoremap 7 7gt +nnoremap 8 8gt +nnoremap 9 9gt +nnoremap 0 :tablast + +" Разбиение окон +nnoremap s :split +nnoremap v :vsplit + +" Выбор окна +nnoremap h +nnoremap j +nnoremap k +nnoremap l + +" Размер окна +nnoremap + +nnoremap - +nnoremap < +nnoremap > + +" Vimspector +" nnoremap +" nnoremap q +nmap VimspectorStepOver +nmap VimspectorStepInto +nmap VimspectorStepOut +nmap VimspectorDisassemble + +" Сделать окна одного размера +nnoremap = = + +" Переключения между буферами +" nnoremap b :bnext +" nnoremap B :bprevious +" nnoremap l :ls +" nnoremap d :bd + +" " Скрыть/раскрыть блок кода +" nnoremap z za + + +" настройка плагинов + +" настройки для отступов +" let g:indent_guides_enable_on_vim_startup = 1 +" Настройки для разноцветной подсветки скобок +let g:rainbow_active = 1 +" Настройки для vim-airline +let g:airline#extensions#tabline#enabled = 1 +let g:airline#extensions#tabline#buffer_nr_show = 1 +let g:airline#extensions#tabline#formatter = 'unique_tail' +let g:airline_powerline_fonts = 1 +let g:airline_solarized_bg = 'luna' + +let g:vimspector_enable_mappings = 'HUMAN' diff --git a/src/main.rs b/src/main.rs index e02c7ae..b374cbe 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,4 +1,4 @@ -use clap::{Arg, Command}; +use clap::{Parser}; pub mod asotr_data { use std::{fs::File, io::Read}; @@ -13,7 +13,7 @@ pub mod asotr_data { lazy_static! { pub static ref support_dtypes: String = String::from(".data01.asotr01(02), data02.asotr01(02), data06.asotr01(02)"); - + pub static ref patterns_fnames_csv_data: Vec<(String, String)> = { let mut patterns: Vec<(String, String)> = Vec::new(); patterns.push((String::from(".*data01.asotr01"), @@ -31,6 +31,17 @@ pub mod asotr_data { patterns }; + + pub static ref patterns_disp: Vec = { + let mut patterns: Vec = Vec::new(); + patterns.push(String::from("ASOTR01 temperature")); + patterns.push(String::from("ASOTR01 power")); + patterns.push(String::from("ASOTR01 temperature setpoint")); + patterns.push(String::from("ASOTR02 temperature")); + patterns.push(String::from("ASOTR02 power")); + patterns.push(String::from("ASOTR02 temperature setpoint")); + patterns + }; } #[derive(Debug, FromRepr, PartialEq)] @@ -42,8 +53,7 @@ pub mod asotr_data { struct AsotrDataDesc { time_s: u64, - // time_mks: u32, - // datetime: String, + time_mks: u32, date: String, time: String, data_type: AsotrDataType, @@ -51,9 +61,9 @@ pub mod asotr_data { } impl AsotrDataDesc { - pub fn new(time_s: u64, date: String, time: String, + pub fn new(time_s: u64, time_mks: u32, date: String, time: String, data_type: AsotrDataType) -> AsotrDataDesc { - AsotrDataDesc { time_s, date, time, data_type } + AsotrDataDesc { time_s, time_mks, date, time, data_type } } } @@ -77,12 +87,11 @@ pub mod asotr_data { Err(msg) => { return Err(format!("Error reading data file {}: {}", filename_full, msg)) } }; - out.push_str(&asotr_head.time_s.to_string()); - out.push(';'); - out.push_str(&asotr_head.date); - out.push(';'); - out.push_str(&asotr_head.time); - out.push(';'); + out.push_str(&format!("{};{} {}.{:02};", + &asotr_head.time_s, + &asotr_head.date, + &asotr_head.time, + asotr_head.time_mks)); if asotr_head.data_type == AsotrDataType::Temp || asotr_head.data_type == AsotrDataType::TempSet { @@ -103,12 +112,12 @@ pub mod asotr_data { out.remove(out.len() - 1); return Ok(out); } - - pub fn parse_data_dir(dir: &str) -> Result<(), String> { + + pub fn parse_data_dir(dir: &str, disp: bool) -> Result<(), String> { let mut data: Vec = Vec::new(); - + println!("parse data from directory: {}", dir); - for (pattern, fname) in patterns_fnames_csv_data.iter() { + for (i, (pattern, fname)) in patterns_fnames_csv_data.iter().enumerate() { let files = find_files_regex(dir, pattern)?; for elem in files { @@ -116,13 +125,15 @@ pub mod asotr_data { } data.sort(); - + + if disp { disp_data(&data, &patterns_disp[i])?; } + println!("save csv data to file: {}", fname); save_data_csv(data.clone(), fname)?; data.clear(); } - + return Ok(()); } @@ -159,19 +170,19 @@ pub mod asotr_data { } fn parse_filename(filename_full: String) -> Result { - let mut filename = String::new(); + let mut fname = String::new(); let msg_prev = format!("Error parsing filename {}:", filename_full); match filename_full.rfind('/') { - Some(val) => { filename = (filename_full[val+1..filename_full.len()]).to_string(); } - _ => { filename = filename_full.clone(); } + Some(val) => { fname = (filename_full[val+1..filename_full.len()]).to_string(); } + _ => { fname = filename_full.clone(); } } - if filename.len() != 32 { + if fname.len() != 32 { return Err(format!("{} unsupported file", msg_prev)); } - let time_unix_ = filename[0..10].parse::(); + let time_unix_ = fname[0..10].parse::(); let time_unix = match &time_unix_ { Ok(data) => data, Err(msg) => { @@ -180,7 +191,7 @@ pub mod asotr_data { } }; - let data_type_ = filename[22..24].parse::(); + let data_type_ = fname[22..24].parse::(); let data_type_u8 = match &data_type_ { Ok(data) => data, Err(msg) => { @@ -208,19 +219,19 @@ pub mod asotr_data { // msg_prev, msg)); } // }; - // let _time_str_mks = filename[11..17].parse::(); - // let time_mks = match &_time_str_mks { - // Ok(data) => data, - // Err(msg) => { return Err(format!("{}: expected digits in timestamp mks part ({})", - // msg_prev, msg)); } - // }; + let _time_str_mks = fname[11..14].parse::(); + let time_mks = match &_time_str_mks { + Ok(data) => data, + Err(msg) => { return Err(format!("{}: expected digits in timestamp mks part ({})", + msg_prev, msg)); } + }; let time: SystemTime = UNIX_EPOCH + Duration::from_secs(*time_unix); let date_time = DateTime::::from(time); let date_s = date_time.format("%d.%m.%Y").to_string(); let time_s = date_time.format("%H:%M:%S").to_string(); - let head = AsotrDataDesc::new(*time_unix, date_s, time_s, data_type); + let head = AsotrDataDesc::new(*time_unix, *time_mks, date_s, time_s, data_type); return Ok(head); } @@ -255,8 +266,31 @@ pub mod asotr_data { return Ok(path_vec); } + fn disp_data(data: &Vec, about: &str) -> Result<(), String> { + println!("{}", about); + println!("{}\t{}\t{}\t{}\t{}\t{}\t{}\t{}", + "timestamp_sec", "timestamp", + "ch1", "ch2", "ch3", "ch4", "ch5", "ch6"); + + for elem in data { + println!("{}", elem.replace(';', "\t")); + } + + return Ok(()) + } + fn save_data_csv(data: Vec, fname: &str) -> Result<(), String> { - match std::fs::write(fname, data.join("\n")){ + + let mut data_w: Vec = Vec::new(); + data_w.push(format!("{};{};{};{};{};{};{};{}", + "timestamp_sec", "timestamp", + "ch1", "ch2", "ch3", "ch4", "ch5", "ch6")); + + for elem in data { + data_w.push(elem); + } + + match std::fs::write(fname, data_w.join("\n")) { Ok(f) => f, Err(msg) => { return Err(format!("Error write data to csv file {}: {}", fname, msg)) @@ -267,50 +301,47 @@ pub mod asotr_data { } } +#[derive(Parser, Debug)] +#[command(version, author, about, long_about = None)] +struct Cli { + /// file with ASOTR MVN data (.data01.asotr01(02), data02.asotr01(02), data06.asotr01(02)) + #[arg(long, short = 'f', value_name = "FILE_DATA")] + filename: Option, + + /// directory with ASOTR MVN data + #[arg(long, short = 'd', value_name = "DIRECTORY_DATA")] + directory: Option, + + /// show data in console + #[arg(short = 's')] + show: bool, +} + fn main() { use crate::asotr_data::*; - let arguments = Command::new("asotr_csv") - .version("0.1.0") - .author("Danila Gamkov ") - .about("raw data ASOTR MVN parser") - .arg( - Arg::new("file") - .short('f') - .long("filename") - .help(format!("file with ASOTR MVN data ({})", support_dtypes.to_string())) - ) - .arg( - Arg::new("dir") - .short('d') - .long("directory") - .help(format!("directory with ASOTR MVN data")) - ) - .get_matches(); + let cli = Cli::parse(); + let show = cli.show; - match arguments.get_one::("file") { - Some(fname) => { - let s = match read_data(fname.clone()) { - Ok(elem) => elem, - Err(msg) => { println!("{}", msg); return; } - }; + if let Some(fname) = &cli.filename { - println!("{}", s); - return; - }, - _ => { } - }; + let s = match read_data(fname.clone()) { + Ok(elem) => elem, + Err(msg) => { println!("{}", msg); return; } + }; - match arguments.get_one::("dir") { - Some(path) => { - match parse_data_dir(path) { - Ok(_) => {} - Err(msg) => { println!("{}", msg); return; } - } - return; - }, - _ => { } + println!("{}", s); + + return; + } + + if let Some(dir) = &cli.directory { + match parse_data_dir(&dir.clone(), show) { + Ok(elem) => elem, + Err(msg) => { println!("{}", msg); return; } + } + return; } println!("Unexpected command. Type --help for more iformation");