Skip to content

Commit

Permalink
Add CSV row formatting
Browse files Browse the repository at this point in the history
  • Loading branch information
sunsided committed Jul 6, 2024
1 parent 07c94dc commit af6dfa7
Showing 1 changed file with 113 additions and 41 deletions.
154 changes: 113 additions & 41 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,12 @@ use async_compression::tokio::write::GzipEncoder;
use async_compression::Level;
use clap::Parser;
use color_eyre::eyre::Result;
use num_traits::real::Real;
pub use ratatui::prelude::*;
use serial_sensors_proto::versions::Version1DataFrame;
use serial_sensors_proto::{deserialize, DeserializationError, SensorData, SensorId};
use serial_sensors_proto::{
deserialize, DeserializationError, IdentifierCode, SensorData, SensorId, ValueType,
};
use tokio::fs::File;
use tokio::io::{self, AsyncReadExt, AsyncWriteExt, BufWriter};
use tokio::sync::mpsc::{unbounded_channel, UnboundedReceiver, UnboundedSender};
Expand Down Expand Up @@ -159,10 +162,14 @@ async fn dump_data(directory: PathBuf, mut rx: UnboundedReceiver<Version1DataFra
println!("Data received: {:?}", data);
let target = SensorId::from(&data);

let data_row = match create_data_row(since_the_epoch, &target, &data) {
None => continue,
Some(data) => data,
};

match files.entry(target.clone()) {
Entry::Occupied(mut entry) => {
let data = create_data_row(since_the_epoch, target, &data);
entry.get_mut().write_all(&data).await?;
entry.get_mut().write_all(&data_row).await?;
}
Entry::Vacant(entry) => {
let file_name = format!(
Expand All @@ -180,67 +187,132 @@ async fn dump_data(directory: PathBuf, mut rx: UnboundedReceiver<Version1DataFra
}
};

let writer = entry.insert(BufWriter::new(file));

// Create header row.
let header = create_header_row(&data);
writer.write_all(&header).await?;

// Create first data row.
let data = create_data_row(since_the_epoch, target, &data);
writer.write_all(&data).await?;

writer.flush().await?;
if let Some(header) = create_header_row(&data) {
let writer = entry.insert(BufWriter::new(file));
writer.write_all(&header).await?;
writer.write_all(&data_row).await?;
writer.flush().await?;
}
}
};
}
}
}

fn create_header_row(data: &Version1DataFrame) -> Vec<u8> {
let mut row = String::from("host_time,sensor_tag,num_components,value_type");
fn create_header_row(data: &Version1DataFrame) -> Option<Vec<u8>> {
let mut row = String::from("host_time,device_time,sensor_tag,num_components,value_type");
match data.value {
SensorData::SystemClockFrequency(_) => {}
SensorData::AccelerometerI16(_) => {}
SensorData::MagnetometerI16(_) => {}
SensorData::TemperatureI16(_) => {}
SensorData::GyroscopeI16(_) => {}
SensorData::HeadingI16(_) => {}
SensorData::EulerAnglesF32(_) => {}
SensorData::OrientationQuaternionF32(_) => {}
SensorData::LinearRanges(_) => {}
SensorData::Identification(_) => {}
SensorData::SystemClockFrequency(_) => row.push_str(",freq"),
SensorData::AccelerometerI16(_) => row.push_str(",x,y,z"),
SensorData::MagnetometerI16(_) => row.push_str(",x,y,z"),
SensorData::TemperatureI16(_) => row.push_str(",temp"),
SensorData::GyroscopeI16(_) => row.push_str(",x,y,z"),
SensorData::HeadingI16(_) => row.push_str(",heading"),
SensorData::EulerAnglesF32(_) => row.push_str(",x,y,z"),
SensorData::OrientationQuaternionF32(_) => row.push_str(",a,b,c,d"),
SensorData::LinearRanges(_) => row.push_str(",resolution_bits,scale_op,scale,scale_raw,scale_decimals,offset,offset_raw,offset_decimals"),
SensorData::Identification(_) => row.push_str(",code,value"),
}
row.push('\n');
row.as_bytes().into()
Some(row.as_bytes().into())
}

fn create_data_row(
since_the_epoch: Duration,
target: SensorId,
target: &SensorId,
data: &Version1DataFrame,
) -> Vec<u8> {
) -> Option<Vec<u8>> {
let device_time = decode_device_time(data);
let mut row = format!(
"{},{:02X},{},{:02X}",
"{},{},{:02X},{},{},",
since_the_epoch.as_secs_f64(),
device_time,
target.tag(),
target.num_components().unwrap_or(0),
target.value_type() as u8
value_type_code(target.value_type())
);
match data.value {
SensorData::SystemClockFrequency(_) => {}
SensorData::AccelerometerI16(_) => {}
SensorData::MagnetometerI16(_) => {}
SensorData::TemperatureI16(_) => {}
SensorData::GyroscopeI16(_) => {}
SensorData::HeadingI16(_) => {}
SensorData::EulerAnglesF32(_) => {}
SensorData::OrientationQuaternionF32(_) => {}
SensorData::LinearRanges(_) => {}
SensorData::Identification(_) => {}
SensorData::SystemClockFrequency(data) => row.push_str(&format!("{}", data.value)),
SensorData::AccelerometerI16(vec) => {
row.push_str(&format!("{},{},{}", vec.x, vec.y, vec.z))
}
SensorData::MagnetometerI16(vec) => row.push_str(&format!("{},{},{}", vec.x, vec.y, vec.z)),
SensorData::TemperatureI16(temp) => row.push_str(&format!("{}", temp.value)),
SensorData::GyroscopeI16(vec) => row.push_str(&format!("{},{},{}", vec.x, vec.y, vec.z)),
SensorData::HeadingI16(temp) => row.push_str(&format!("{}", temp.value)),
SensorData::EulerAnglesF32(vec) => row.push_str(&format!("{},{},{}", vec.x, vec.y, vec.z)),
SensorData::OrientationQuaternionF32(vec) => {
row.push_str(&format!("{},{},{},{}", vec.a, vec.b, vec.c, vec.d))
}
SensorData::LinearRanges(ref lr) => row.push_str(&format!(
"{},{:02X},{},{},{},{},{},{}",
lr.resolution_bits,
lr.scale_op,
lr.scale as f32 * 10.0.powi(-(lr.scale_decimals as i32)),
lr.scale,
lr.scale_decimals,
lr.offset as f32 * 10.0.powi(-(lr.offset_decimals as i32)),
lr.offset,
lr.offset_decimals
)),
SensorData::Identification(ref ident) => row.push_str(&format!(
"{},{}",
ident_code(ident.code),
std::str::from_utf8(&ident.value).unwrap_or("").trim()
)),
}
row.push('\n');
row.as_bytes().into()
Some(row.as_bytes().into())
}

fn decode_device_time(data: &Version1DataFrame) -> f32 {
if data.system_secs != u32::MAX {
data.system_secs as f32
+ if data.system_millis != u16::MAX {
data.system_millis as f32 / 1_000.0
} else {
0.0
}
+ if data.system_nanos != u16::MAX {
data.system_nanos as f32 / 1_000_000.0
} else {
0.0
}
} else {
0.0
}
}

fn ident_code(code: IdentifierCode) -> &'static str {
match code {
IdentifierCode::Generic => "generic",
IdentifierCode::Maker => "maker",
IdentifierCode::Product => "product",
IdentifierCode::Revision => "revision",
}
}

fn value_type_code(vt: ValueType) -> &'static str {
match vt {
ValueType::UInt8 => "u8",
ValueType::SInt8 => "i8",
ValueType::UInt16 => "u16",
ValueType::SInt16 => "i16",
ValueType::UInt32 => "u32",
ValueType::SInt32 => "i32",
ValueType::UInt64 => "u64",
ValueType::SInt64 => "i64",
ValueType::UInt128 => "u128",
ValueType::SInt128 => "i128",
ValueType::Float32 => "f32",
ValueType::Float64 => "f64",
ValueType::Q8_8 => "Q8.8",
ValueType::Q16_16 => "Q16.1",
ValueType::Q32_32 => "Q32.32",
ValueType::LinearRange => "linear range",
ValueType::Identifier => "ident",
}
}

async fn decoder(
Expand Down

0 comments on commit af6dfa7

Please sign in to comment.