project reorganization: 1. executable files in bin directory now. 2. add recursive_unpack_targz.py for recursive unpacking specified in this script archives tar.gz with MVN data. 3. add asotr_unzip_plot.sh bash file for unpacking MVN data, collect asotr data into csv files and plot asotr MVN data. 4. add brd_wheel_1Hz_parser.py for demonstrate how to work with brd telemetry data
This commit is contained in:
132
asotr_csv/src/.vimrc
Normal file
132
asotr_csv/src/.vimrc
Normal file
@@ -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 <C-h> <Left>
|
||||
inoremap <C-j> <Down>
|
||||
inoremap <C-k> <Up>
|
||||
inoremap <C-l> <Right>
|
||||
|
||||
let g:mapleader = "\<Space>"
|
||||
|
||||
" Переключение между вкладками
|
||||
nnoremap <leader>t :tabnext<CR>
|
||||
nnoremap <leader>T :tabprevious<CR>
|
||||
|
||||
" Список вкладок
|
||||
nnoremap <leader>tl :tabs<CR>
|
||||
|
||||
" nnoremap <leader>tn :tabnew<CR>
|
||||
nnoremap <leader>tc :tabclose<CR>
|
||||
nnoremap <leader>to :tabonly<CR>
|
||||
nnoremap <leader>tm :tabmove<CR>
|
||||
|
||||
" Редактировать файл в новой вкладке
|
||||
nnoremap <leader>te :tabedit |
|
||||
|
||||
" Выбор вкладки
|
||||
nnoremap <leader>1 1gt
|
||||
nnoremap <leader>2 2gt
|
||||
nnoremap <leader>3 3gt
|
||||
nnoremap <leader>4 4gt
|
||||
nnoremap <leader>5 5gt
|
||||
nnoremap <leader>6 6gt
|
||||
nnoremap <leader>7 7gt
|
||||
nnoremap <leader>8 8gt
|
||||
nnoremap <leader>9 9gt
|
||||
nnoremap <leader>0 :tablast<CR>
|
||||
|
||||
" Разбиение окон
|
||||
nnoremap <leader>s :split<CR>
|
||||
nnoremap <leader>v :vsplit<CR>
|
||||
|
||||
" Выбор окна
|
||||
nnoremap <C-h> <C-w>h
|
||||
nnoremap <C-j> <C-w>j
|
||||
nnoremap <C-k> <C-w>k
|
||||
nnoremap <C-l> <C-w>l
|
||||
|
||||
" Размер окна
|
||||
nnoremap <C-u> <C-w>+
|
||||
nnoremap <C-d> <C-w>-
|
||||
nnoremap <C-p> <C-w><
|
||||
nnoremap <C-n> <C-w>>
|
||||
|
||||
" Vimspector
|
||||
" nnoremap <leader><F2> <F10>
|
||||
" nnoremap <leader>q <F11>
|
||||
nmap <Leader><Right> <Plug>VimspectorStepOver
|
||||
nmap <Leader><Down> <Plug>VimspectorStepInto
|
||||
nmap <Leader><Up> <Plug>VimspectorStepOut
|
||||
nmap <Leader><Tab> <Plug>VimspectorDisassemble
|
||||
|
||||
" Сделать окна одного размера
|
||||
nnoremap <leader>= <C-w>=
|
||||
|
||||
" Переключения между буферами
|
||||
" nnoremap <leader>b :bnext<CR>
|
||||
" nnoremap <leader>B :bprevious<CR>
|
||||
" nnoremap <leader>l :ls<CR>
|
||||
" nnoremap <leader>d :bd<CR>
|
||||
|
||||
" " Скрыть/раскрыть блок кода
|
||||
" nnoremap <leader>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'
|
355
asotr_csv/src/main.rs
Normal file
355
asotr_csv/src/main.rs
Normal file
@@ -0,0 +1,355 @@
|
||||
use clap::{Parser};
|
||||
|
||||
pub mod asotr_data {
|
||||
use std::{fs::File, io::Read};
|
||||
use byteorder::{LittleEndian, ReadBytesExt};
|
||||
use chrono::{DateTime, Utc};
|
||||
use std::time::{SystemTime, UNIX_EPOCH, Duration};
|
||||
use strum::FromRepr;
|
||||
use lazy_static::lazy_static;
|
||||
use regex::Regex;
|
||||
use walkdir::WalkDir;
|
||||
|
||||
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"),
|
||||
String::from("../data/asotr/asotr01_data_T.csv")));
|
||||
patterns.push((String::from(".*data02.asotr01"),
|
||||
String::from("../data/asotr/asotr01_data_P.csv")));
|
||||
patterns.push((String::from(".*data06.asotr01"),
|
||||
String::from("../data/asotr/asotr01_data_TSET.csv")));
|
||||
patterns.push((String::from(".*data01.asotr02"),
|
||||
String::from("../data/asotr/asotr02_data_T.csv")));
|
||||
patterns.push((String::from(".*data02.asotr02"),
|
||||
String::from("../data/asotr/asotr02_data_P.csv")));
|
||||
patterns.push((String::from(".*data06.asotr02"),
|
||||
String::from("../data/asotr/asotr02_data_TSET.csv")));
|
||||
|
||||
patterns
|
||||
};
|
||||
|
||||
pub static ref patterns_disp: Vec<String> = {
|
||||
let mut patterns: Vec<String> = 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)]
|
||||
enum AsotrDataType {
|
||||
Temp = 1,
|
||||
Pow = 2,
|
||||
TempSet = 6,
|
||||
}
|
||||
|
||||
struct AsotrDataDesc {
|
||||
time_s: u64,
|
||||
time_mks: u32,
|
||||
date: String,
|
||||
time: String,
|
||||
data_type: AsotrDataType,
|
||||
// kit: u8,
|
||||
}
|
||||
|
||||
impl AsotrDataDesc {
|
||||
pub fn new(time_s: u64, time_mks: u32, date: String, time: String,
|
||||
data_type: AsotrDataType) -> AsotrDataDesc {
|
||||
AsotrDataDesc { time_s, time_mks, date, time, data_type }
|
||||
}
|
||||
}
|
||||
|
||||
pub fn read_data(filename_full: String) -> Result<String, String> {
|
||||
let ch_u16: [u16; 6];
|
||||
let ch_f32: [f32; 6];
|
||||
|
||||
let asotr_head = parse_filename(filename_full.clone())?;
|
||||
|
||||
let mut buf = Vec::new();
|
||||
let mut out = String::new();
|
||||
|
||||
let mut data = match File::open(filename_full.clone())
|
||||
{
|
||||
Ok(file) => file,
|
||||
Err(msg) => { return Err(format!("Error opening data file {}: {}", filename_full, msg)) }
|
||||
};
|
||||
|
||||
match data.read_to_end(&mut buf) {
|
||||
Ok(stat) => stat,
|
||||
Err(msg) => { return Err(format!("Error reading data file {}: {}", filename_full, msg)) }
|
||||
};
|
||||
|
||||
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 {
|
||||
ch_f32 = parse_data_f32(buf)?;
|
||||
for elem in ch_f32 {
|
||||
out.push_str(&elem.to_string());
|
||||
out.push(';');
|
||||
}
|
||||
}
|
||||
else if asotr_head.data_type == AsotrDataType::Pow {
|
||||
ch_u16 = parse_data_u16(buf)?;
|
||||
for elem in ch_u16 {
|
||||
out.push_str(&elem.to_string());
|
||||
out.push(';');
|
||||
}
|
||||
}
|
||||
|
||||
out.remove(out.len() - 1);
|
||||
return Ok(out);
|
||||
}
|
||||
|
||||
pub fn parse_data_dir(dir: &str, disp: bool) -> Result<(), String> {
|
||||
let mut data: Vec<String> = Vec::new();
|
||||
|
||||
println!("parse data from directory: {}", dir);
|
||||
for (i, (pattern, fname)) in patterns_fnames_csv_data.iter().enumerate() {
|
||||
let files = find_files_regex(dir, pattern)?;
|
||||
|
||||
for elem in files {
|
||||
data.push(read_data(elem)?);
|
||||
}
|
||||
|
||||
data.sort();
|
||||
data.dedup();
|
||||
|
||||
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(());
|
||||
}
|
||||
|
||||
fn parse_data_f32(buf: Vec<u8>) -> Result<[f32; 6], String> {
|
||||
let mut data = &buf[..];
|
||||
let mut ch: [f32; 6] = [0.0; 6];
|
||||
|
||||
for i in 0..6 {
|
||||
ch[i] = match data.read_f32::<LittleEndian>() {
|
||||
Ok(val) => val,
|
||||
Err(msg) => {
|
||||
return Err(format!(
|
||||
"Error parsing file: failed parsing float32 data: {}", msg)); }
|
||||
}
|
||||
}
|
||||
|
||||
return Ok(ch);
|
||||
}
|
||||
|
||||
fn parse_data_u16(buf: Vec<u8>) -> Result<[u16; 6], String> {
|
||||
let mut data = &buf[..];
|
||||
let mut ch: [u16; 6] = [0; 6];
|
||||
|
||||
for i in 0..6 {
|
||||
ch[i] = match data.read_u16::<LittleEndian>() {
|
||||
Ok(val) => val,
|
||||
Err(msg) => {
|
||||
return Err(format!(
|
||||
"Error parsing file: failed parsing uint16 data: {}", msg)); }
|
||||
}
|
||||
}
|
||||
|
||||
return Ok(ch);
|
||||
}
|
||||
|
||||
fn parse_filename(filename_full: String) -> Result<AsotrDataDesc, String> {
|
||||
let mut fname = String::new();
|
||||
let msg_prev = format!("Error parsing filename {}:", filename_full);
|
||||
|
||||
match filename_full.rfind('/') {
|
||||
Some(val) => { fname = (filename_full[val+1..filename_full.len()]).to_string(); }
|
||||
_ => { fname = filename_full.clone(); }
|
||||
}
|
||||
|
||||
if fname.len() != 32 {
|
||||
return Err(format!("{} unsupported file", msg_prev));
|
||||
}
|
||||
|
||||
let time_unix_ = fname[0..10].parse::<u64>();
|
||||
let time_unix = match &time_unix_ {
|
||||
Ok(data) => data,
|
||||
Err(msg) => {
|
||||
return Err(format!("{} expected digits in timestamp sec part ({})",
|
||||
msg_prev, msg));
|
||||
}
|
||||
};
|
||||
|
||||
let data_type_ = fname[22..24].parse::<u8>();
|
||||
let data_type_u8 = match &data_type_ {
|
||||
Ok(data) => data,
|
||||
Err(msg) => {
|
||||
return Err(format!("{} expected digits in data type part ({})",
|
||||
msg_prev, msg));
|
||||
}
|
||||
};
|
||||
|
||||
if *data_type_u8 == 1 || *data_type_u8 == 2 || *data_type_u8 == 6 { }
|
||||
else {
|
||||
return Err(format!("{} parser supports data types: {}",
|
||||
msg_prev, support_dtypes.to_string()));
|
||||
}
|
||||
|
||||
let data_type = match AsotrDataType::from_repr(*data_type_u8 as usize) {
|
||||
Some(value) => value,
|
||||
_ => return Err(format!("{} expected digits in data type part",
|
||||
msg_prev))
|
||||
};
|
||||
|
||||
// let _kit = filename[30..32].parse::<u8>();
|
||||
// let kit = match &_kit {
|
||||
// Ok(data) => data,
|
||||
// Err(msg) => { return Err(format!("{}: expected digits in asotr kit part ({})",
|
||||
// msg_prev, msg)); }
|
||||
// };
|
||||
|
||||
let _time_str_mks = fname[11..14].parse::<u32>();
|
||||
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::<Utc>::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, *time_mks, date_s, time_s, data_type);
|
||||
|
||||
return Ok(head);
|
||||
}
|
||||
|
||||
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 disp_data(data: &Vec<String>, 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<String>, fname: &str) -> Result<(), String> {
|
||||
|
||||
let mut data_w: Vec<String> = 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))
|
||||
}
|
||||
}
|
||||
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
|
||||
#[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<String>,
|
||||
|
||||
/// directory with ASOTR MVN data
|
||||
#[arg(long, short = 'd', value_name = "DIRECTORY_DATA")]
|
||||
directory: Option<String>,
|
||||
|
||||
/// show data in console
|
||||
#[arg(short = 's')]
|
||||
show: bool,
|
||||
}
|
||||
|
||||
|
||||
fn main() {
|
||||
use crate::asotr_data::*;
|
||||
|
||||
let cli = Cli::parse();
|
||||
let show = cli.show;
|
||||
|
||||
if let Some(fname) = &cli.filename {
|
||||
|
||||
let s = match read_data(fname.clone()) {
|
||||
Ok(elem) => elem,
|
||||
Err(msg) => { println!("{}", msg); 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");
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
Reference in New Issue
Block a user