use anyhow::{anyhow, Result};
use csv::StringRecord;
use itertools::Itertools;
use std::str::FromStr;
pub trait Csv {
fn csv(&self) -> Vec<StringRecord>;
}
impl Csv for StringRecord {
fn csv(&self) -> Vec<StringRecord> {
vec![self.clone()]
}
}
#[derive(Debug, Clone)]
pub struct DataPacket {
pub host_timestamp: f64,
pub emotibit_timestamp: f64,
pub packet_id: u32,
pub data_points: u8,
pub version: u8,
pub reliability: u8,
pub data_type: DataType,
}
impl Csv for DataPacket {
fn csv(&self) -> Vec<StringRecord> {
let mut vec = Vec::new();
let payload = Self::parse_data_type(&self.data_type, self.data_type.payload());
for p in payload {
vec.push(StringRecord::from(vec![
self.host_timestamp.to_string(),
self.emotibit_timestamp.to_string(),
self.packet_id.to_string(),
self.data_points.to_string(),
self.data_type.as_str().to_owned(),
self.version.to_string(),
self.reliability.to_string(),
p.to_string(),
]));
}
vec
}
}
impl DataPacket {
fn parse_data_type(data_type: &DataType, payload: Vec<String>) -> Vec<String> {
use DataType::*;
match data_type {
TxLcLm(_) | TxTlLc(_) => vec![format!(
"{},{}",
payload.get(0).unwrap(),
payload.get(1).unwrap()
)],
_ => payload,
}
}
pub fn inject_host_timestamp(self, map: &TimeSyncMap) -> Self {
let timestamp = map.tl0
+ (map.tl1 - map.tl0) * (self.emotibit_timestamp - map.te0) / (map.te1 - map.te0);
DataPacket {
host_timestamp: timestamp,
emotibit_timestamp: self.emotibit_timestamp,
packet_id: self.packet_id,
data_points: self.data_points,
version: self.version,
reliability: self.reliability,
data_type: self.data_type,
}
}
}
impl TryFrom<&StringRecord> for DataPacket {
type Error = anyhow::Error;
fn try_from(r: &StringRecord) -> Result<Self, Self::Error> {
if let (
Some(timestamp),
Some(packet_id),
Some(data_points),
Some(data_type),
Some(version),
Some(reliability),
) = (r.get(0), r.get(1), r.get(2), r.get(3), r.get(4), r.get(5))
{
Ok(DataPacket {
host_timestamp: f64::NAN,
emotibit_timestamp: timestamp.parse()?,
packet_id: packet_id.parse()?,
data_points: data_points.parse()?,
version: version.parse()?,
reliability: reliability.parse()?,
data_type: get_data_type(r, data_type)?,
})
} else {
Err(anyhow!("Missing Column, record: {:?}", r))
}
}
}
impl TryFrom<&str> for DataPacket {
type Error = anyhow::Error;
fn try_from(str: &str) -> Result<Self, Self::Error> {
let r: Vec<&str> = str.split(',').collect();
let r = csv::ByteRecord::from(r);
let r = &StringRecord::from_byte_record(r)?;
r.try_into()
}
}
#[test]
fn string_to_data() {
let input = "1126349,49106,10,PI,1,100,156593,156471,156372,156300,156205,156136,156130,156103,156051,156103";
let packet: DataPacket = input.try_into().unwrap();
assert_eq!(packet.packet_id, 49106);
assert_eq!(packet.data_points, 10);
}
#[derive(Debug, Clone, PartialEq)]
pub enum DataType {
EA(Vec<f32>),
EL(Vec<f32>),
ER(Vec<f32>),
PI(Vec<u32>),
PR(Vec<u32>),
PG(Vec<u32>),
T0(Vec<f32>),
T1(Vec<f32>),
TH(Vec<f32>),
AX(Vec<f32>),
AY(Vec<f32>),
AZ(Vec<f32>),
GX(Vec<f32>),
GY(Vec<f32>),
GZ(Vec<f32>),
MX(Vec<i32>),
MY(Vec<i32>),
MZ(Vec<i32>),
BV(Vec<f32>),
BATLV(Vec<u32>),
AK(Vec<String>),
RD(Vec<String>),
TL(String),
TX(Vec<String>),
TxTlLc((String, f32)),
TxLcLm(Vec<f32>),
EM(Vec<String>),
HR(Vec<i32>),
BI(Vec<i32>),
SA(Vec<f32>),
SF(Vec<f32>),
SR(Vec<f32>),
UN(Vec<String>),
LM(String),
RB(String),
}
impl DataType {
pub fn as_str(&self) -> &'static str {
use DataType::*;
match self {
EA(_) => "EA",
EL(_) => "EL",
ER(_) => "ER",
PI(_) => "PI",
PR(_) => "PR",
PG(_) => "PG",
T0(_) => "T0",
T1(_) => "T1",
TH(_) => "TH",
AX(_) => "AX",
AY(_) => "AY",
AZ(_) => "AZ",
GX(_) => "GX",
GY(_) => "GY",
GZ(_) => "GZ",
MX(_) => "MX",
MY(_) => "MY",
MZ(_) => "MZ",
BV(_) => "BV",
BATLV(_) => "B%",
AK(_) => "AK",
RD(_) => "RD",
TL(_) => "TL",
TX(_) => "TX",
TxTlLc(_) => "TX_TL_LC",
TxLcLm(_) => "TX_LC_LM",
EM(_) => "EM",
HR(_) => "HR",
BI(_) => "BI",
SA(_) => "SA",
SF(_) => "SF",
SR(_) => "SR",
UN(_) => "UN",
LM(_) => "LM",
RB(_) => "RB",
}
}
pub fn payload(&self) -> Vec<String> {
use DataType::*;
match self {
EA(v) => v.iter().map(|p| p.to_string()).collect(),
EL(v) => v.iter().map(|p| p.to_string()).collect(),
ER(v) => v.iter().map(|p| p.to_string()).collect(),
PI(v) => v.iter().map(|p| p.to_string()).collect(),
PR(v) => v.iter().map(|p| p.to_string()).collect(),
PG(v) => v.iter().map(|p| p.to_string()).collect(),
T0(v) => v.iter().map(|p| p.to_string()).collect(),
T1(v) => v.iter().map(|p| p.to_string()).collect(),
TH(v) => v.iter().map(|p| p.to_string()).collect(),
AX(v) => v.iter().map(|p| p.to_string()).collect(),
AY(v) => v.iter().map(|p| p.to_string()).collect(),
AZ(v) => v.iter().map(|p| p.to_string()).collect(),
GX(v) => v.iter().map(|p| p.to_string()).collect(),
GY(v) => v.iter().map(|p| p.to_string()).collect(),
GZ(v) => v.iter().map(|p| p.to_string()).collect(),
MX(v) => v.iter().map(|p| p.to_string()).collect(),
MY(v) => v.iter().map(|p| p.to_string()).collect(),
MZ(v) => v.iter().map(|p| p.to_string()).collect(),
BV(v) => v.iter().map(|p| p.to_string()).collect(),
BATLV(v) => v.iter().map(|p| p.to_string()).collect(),
AK(sv) => sv.to_vec(),
RD(sv) => sv.to_vec(),
TL(s) => vec![s.to_owned()],
TX(sv) => sv.to_vec(),
TxTlLc((s, f)) => vec![s.to_owned(), f.to_string()],
TxLcLm(v) => v.iter().map(|p| p.to_string()).collect(),
EM(sv) => sv.to_vec(),
HR(v) => v.iter().map(|p| p.to_string()).collect(),
BI(v) => v.iter().map(|p| p.to_string()).collect(),
SA(v) => v.iter().map(|p| p.to_string()).collect(),
SF(v) => v.iter().map(|p| p.to_string()).collect(),
SR(v) => v.iter().map(|p| p.to_string()).collect(),
UN(sv) => sv.to_vec(),
LM(s) => vec![s.to_owned()],
RB(s) => vec![s.to_owned()],
}
}
}
fn get_data_type(record: &StringRecord, type_str: &str) -> Result<DataType> {
let skip_to_payload = 6_usize;
match type_str {
"RB" => Ok(DataType::RB(to_string(record, skip_to_payload))),
"AK" => Ok(DataType::AK(to_string_vec(record, skip_to_payload))),
"EA" => Ok(DataType::EA(to_vec::<f32>(record, skip_to_payload)?)),
"EL" => Ok(DataType::EL(to_vec::<f32>(record, skip_to_payload)?)),
"ER" => Ok(DataType::ER(to_vec::<f32>(record, skip_to_payload)?)),
"PI" => Ok(DataType::PI(to_vec::<u32>(record, skip_to_payload)?)),
"PR" => Ok(DataType::PR(to_vec::<u32>(record, skip_to_payload)?)),
"PG" => Ok(DataType::PG(to_vec::<u32>(record, skip_to_payload)?)),
"T0" => Ok(DataType::T0(to_vec::<f32>(record, skip_to_payload)?)),
"T1" => Ok(DataType::T1(to_vec::<f32>(record, skip_to_payload)?)),
"TH" => Ok(DataType::TH(to_vec::<f32>(record, skip_to_payload)?)),
"AX" => Ok(DataType::AX(to_vec::<f32>(record, skip_to_payload)?)),
"AY" => Ok(DataType::AY(to_vec::<f32>(record, skip_to_payload)?)),
"AZ" => Ok(DataType::AZ(to_vec::<f32>(record, skip_to_payload)?)),
"GX" => Ok(DataType::GX(to_vec::<f32>(record, skip_to_payload)?)),
"GY" => Ok(DataType::GY(to_vec::<f32>(record, skip_to_payload)?)),
"GZ" => Ok(DataType::GZ(to_vec::<f32>(record, skip_to_payload)?)),
"MX" => Ok(DataType::MX(to_vec::<i32>(record, skip_to_payload)?)),
"MY" => Ok(DataType::MY(to_vec::<i32>(record, skip_to_payload)?)),
"MZ" => Ok(DataType::MZ(to_vec::<i32>(record, skip_to_payload)?)),
"BI" => Ok(DataType::BI(to_vec::<i32>(record, skip_to_payload)?)),
"SA" => Ok(DataType::SA(to_vec::<f32>(record, skip_to_payload)?)),
"SF" => Ok(DataType::SF(to_vec::<f32>(record, skip_to_payload)?)),
"SR" => Ok(DataType::SR(to_vec::<f32>(record, skip_to_payload)?)),
"BV" => Ok(DataType::BV(to_vec::<f32>(record, skip_to_payload)?)),
"HR" => Ok(DataType::HR(to_vec::<i32>(record, skip_to_payload)?)),
"B%" => Ok(DataType::BATLV(to_vec::<u32>(record, skip_to_payload)?)),
"RD" => Ok(DataType::RD(to_string_vec(record, skip_to_payload))),
"UN" => Ok(DataType::UN(to_string_vec(record, skip_to_payload))),
"EM" => Ok(DataType::EM(to_string_vec(record, skip_to_payload))),
"TL" => Ok(DataType::TL(to_string(record, skip_to_payload))),
"TX" => Ok(DataType::TX(to_string_vec(record, skip_to_payload))),
"LM" => Ok(DataType::LM(to_string(record, skip_to_payload))),
_ => Err(anyhow!("Unknown Type: {}, {:?}", type_str, record)),
}
}
fn to_string(record: &StringRecord, index: usize) -> String {
record.iter().skip(index).join(",")
}
fn to_vec<T>(record: &StringRecord, index_from: usize) -> Result<Vec<T>>
where
T: num::Num + FromStr,
<T as std::str::FromStr>::Err: std::fmt::Debug,
{
let mut errors = vec![];
let vec = record
.iter()
.skip(index_from)
.map(|x| x.to_string().trim().parse::<T>())
.filter_map(|r| r.map_err(|e| errors.push(e)).ok())
.collect::<Vec<T>>();
if errors.is_empty() {
Ok(vec)
} else {
Err(anyhow!("Parse to Num Error: {:?}", record))
}
}
fn to_string_vec(record: &StringRecord, index_from: usize) -> Vec<String> {
record
.iter()
.skip(index_from)
.map(|str| str.to_owned())
.collect()
}
#[derive(Debug, Clone)]
pub struct TimeSync {
pub rd: f64,
pub ts_received: f64,
pub ts_sent: String,
pub ak: f64,
pub round_trip: f64,
}
impl Csv for TimeSync {
fn csv(&self) -> Vec<StringRecord> {
vec![StringRecord::from(vec![
self.rd.to_string(),
self.ts_received.to_string(),
self.ts_sent.to_owned(),
self.ak.to_string(),
self.round_trip.to_string(),
])]
}
}
#[derive(Debug)]
pub struct TimeSyncMap {
pub te0: f64,
pub te1: f64,
pub tl0: f64,
pub tl1: f64,
pub syncs_received: usize,
pub emotibit_start_time: f64,
pub emotibit_end_time: f64,
pub parse_version: String,
}
impl Csv for TimeSyncMap {
fn csv(&self) -> Vec<StringRecord> {
vec![StringRecord::from(vec![
self.te0.to_string(),
self.te1.to_string(),
self.tl0.to_string(),
self.tl1.to_string(),
self.syncs_received.to_string(),
self.emotibit_start_time.to_string(),
self.emotibit_end_time.to_string(),
self.parse_version.to_owned(),
])]
}
}