diff --git a/scripts/tdf_decoder.rs.jinja b/scripts/tdf_decoder.rs.jinja index 7911d2e..3cb2e3c 100644 --- a/scripts/tdf_decoder.rs.jinja +++ b/scripts/tdf_decoder.rs.jinja @@ -1,7 +1,5 @@ use std::io::{Cursor, Read, Result, Error, ErrorKind}; -use byteorder::{LittleEndian, BigEndian, ReadBytesExt}; - pub fn tdf_name(tdf_id: &u16) -> String { match tdf_id { @@ -12,17 +10,7 @@ pub fn tdf_name(tdf_id: &u16) -> String } } -pub fn tdf_fields(tdf_id: &u16) -> Vec<&'static str> -{ - match tdf_id { -{% for tdf_id, info in definitions.items() %} - {{ tdf_id }} => vec![{{ info['rust_head'] }}], -{% endfor %} - _ => vec!["unknown"], - } -} - -fn vla_bytes_remaining(cursor: &mut Cursor<&[u8]>, cursor_start: u64, size: u8) -> Result +pub fn vla_bytes_remaining(cursor: &mut Cursor<&[u8]>, cursor_start: u64, size: u8) -> Result { let cursor_current = cursor.position(); let cursor_read = cursor_current - cursor_start; @@ -37,7 +25,7 @@ fn vla_bytes_remaining(cursor: &mut Cursor<&[u8]>, cursor_start: u64, size: u8) Ok(bytes_remaining as usize) } -fn tdf_field_read_string(cursor: &mut Cursor<&[u8]>, cursor_start: u64, num: u8, size: u8) -> Result +pub fn tdf_field_read_string(cursor: &mut Cursor<&[u8]>, cursor_start: u64, num: u8, size: u8) -> Result> { let string_length = match num { 0 => vla_bytes_remaining(cursor, cursor_start, size)?, @@ -47,57 +35,14 @@ fn tdf_field_read_string(cursor: &mut Cursor<&[u8]>, cursor_start: u64, num: u8, let mut buf = vec![0u8; string_length]; cursor.read_exact(&mut buf)?; - match String::from_utf8(buf) { - Ok(val) => Ok(format!("\"{}\"", val.trim_matches(char::from(0)))), - Err(..) => Ok(String::from("\"\"")) - } + Ok(buf) } -fn tdf_field_read_vla(cursor: &mut Cursor<&[u8]>, cursor_start: u64, size: u8) -> Result +pub fn tdf_field_read_vla(cursor: &mut Cursor<&[u8]>, cursor_start: u64, size: u8) -> Result> { let bytes_remaining = vla_bytes_remaining(cursor, cursor_start, size)?; let mut buf = vec![0u8; bytes_remaining]; cursor.read_exact(&mut buf)?; - Ok(format!("{}", hex::encode(buf))) -} - -pub fn tdf_read_into_str(tdf_id: &u16, size: u8, cursor: &mut Cursor<&[u8]>) -> Result -{ - let cursor_start = cursor.position(); - - let res = match tdf_id { -{% for tdf_id, info in definitions.items() %} - {{ tdf_id }} => - Ok(format!( - "{{ info['rust_fmt'] }}", -{% for conv in info['rust_convs'] %} - {{ conv[1] }}, -{% endfor %} - )), -{% endfor %} - _ => { - let mut buf = vec![0; size as usize]; - cursor.read_exact(&mut buf)?; - Ok(format!("{}", hex::encode(buf))) - } - }; - let cursor_end = cursor.position(); - let cursor_read = cursor_end - cursor_start; - - if (size as u64) < cursor_read { - // Hande read overflow (more data read than specified) - return Result::Err(Error::new( - ErrorKind::InvalidData, - "Read overflow, corrupt data/metadata", - )); - } - - let underflow = size as u64 - cursor_read; - - // Handle read underflow (more data specified than expected) - if underflow > 0 { - tdf_field_read_string(cursor, cursor_start, 0, underflow as u8)?; - } - res + Ok(buf) } diff --git a/scripts/tdf_decoder_build.py b/scripts/tdf_decoder_build.py index 99714ba..b92230a 100644 --- a/scripts/tdf_decoder_build.py +++ b/scripts/tdf_decoder_build.py @@ -33,7 +33,8 @@ def decoders_gen(tdf_defs, output): trim_blocks=True, lstrip_blocks=True, ) - tdf_template = env.get_template("tdf_decoder.rs.jinja") + common_template = env.get_template("tdf_decoder.rs.jinja") + csv_template = env.get_template("tdf_decoder_csv.rs.jinja") def field_conv_func(field, name_prefix=None): t = rust_type[field["type"]] @@ -80,12 +81,14 @@ def field_conv_func(field, name_prefix=None): return [ ( n, - f"tdf_field_read_string(cursor, cursor_start, {field['num']}, size)?", + f"tdf_field_read_string_to_str(cursor, cursor_start, {field['num']}, size)?", ) ] else: if field["num"] == 0: - return [(n, "tdf_field_read_vla(cursor, cursor_start, size)?")] + return [ + (n, "tdf_field_read_vla_to_str(cursor, cursor_start, size)?") + ] else: return [(n + f"[{idx}]", func) for idx in range(field["num"])] else: @@ -117,7 +120,7 @@ def field_fmt(field): struct_fmts[f"struct {name}"] = fmts # Generate rust conversion functions - for tdf_id, info in tdf_defs["definitions"].items(): + for _tdf_id, info in tdf_defs["definitions"].items(): info["rust_convs"] = [] fmt = [] for f in info["fields"]: @@ -133,13 +136,20 @@ def field_fmt(field): info["rust_head"] = ",".join([f'"{c[0]}"' for c in info["rust_convs"]]) info["rust_fmt"] = ",".join(fmt) - tdf_output = pathlib.Path(output) / "decoders.rs" - with tdf_output.open("w") as f: - f.write( - tdf_template.render( - structs=tdf_defs["structs"], definitions=tdf_defs["definitions"] - ) + common_output = pathlib.Path(output) / "decoders.rs" + csv_output = pathlib.Path(output) / "decoders_csv.rs" + with common_output.open("w") as f: + rendered = common_template.render( + structs=tdf_defs["structs"], definitions=tdf_defs["definitions"] ) + f.write(rendered) + f.write(os.linesep) + + with csv_output.open("w") as f: + rendered = csv_template.render( + structs=tdf_defs["structs"], definitions=tdf_defs["definitions"] + ) + f.write(rendered) f.write(os.linesep) diff --git a/scripts/tdf_decoder_csv.rs.jinja b/scripts/tdf_decoder_csv.rs.jinja new file mode 100644 index 0000000..13df6bf --- /dev/null +++ b/scripts/tdf_decoder_csv.rs.jinja @@ -0,0 +1,70 @@ +use std::io::{Cursor, Read, Result, Error, ErrorKind}; + +use byteorder::{LittleEndian, BigEndian, ReadBytesExt}; + +pub fn tdf_fields(tdf_id: &u16) -> Vec<&'static str> +{ + match tdf_id { +{% for tdf_id, info in definitions.items() %} + {{ tdf_id }} => vec![{{ info['rust_head'] }}], +{% endfor %} + _ => vec!["unknown"], + } +} + +fn tdf_field_read_string_to_str(cursor: &mut Cursor<&[u8]>, cursor_start: u64, num: u8, size: u8) -> Result +{ + let buf = crate::decoders::tdf_field_read_string(cursor, cursor_start, num, size)?; + + match String::from_utf8(buf) { + Ok(val) => Ok(format!("\"{}\"", val.trim_matches(char::from(0)))), + Err(..) => Ok(String::from("\"\"")) + } +} + +fn tdf_field_read_vla_to_str(cursor: &mut Cursor<&[u8]>, cursor_start: u64, size: u8) -> Result +{ + let buf = crate::decoders::tdf_field_read_vla(cursor, cursor_start, size)?; + + Ok(format!("{}", hex::encode(buf))) +} + +pub fn tdf_read_into_str(tdf_id: &u16, size: u8, cursor: &mut Cursor<&[u8]>) -> Result +{ + let cursor_start = cursor.position(); + + let res = match tdf_id { +{% for tdf_id, info in definitions.items() %} + {{ tdf_id }} => + Ok(format!( + "{{ info['rust_fmt'] }}", +{% for conv in info['rust_convs'] %} + {{ conv[1] }}, +{% endfor %} + )), +{% endfor %} + _ => { + let mut buf = vec![0; size as usize]; + cursor.read_exact(&mut buf)?; + Ok(format!("{}", hex::encode(buf))) + } + }; + let cursor_end = cursor.position(); + let cursor_read = cursor_end - cursor_start; + + if (size as u64) < cursor_read { + // Hande read overflow (more data read than specified) + return Result::Err(Error::new( + ErrorKind::InvalidData, + "Read overflow, corrupt data/metadata", + )); + } + + let underflow = size as u64 - cursor_read; + + // Handle read underflow (more data specified than expected) + if underflow > 0 { + crate::decoders::tdf_field_read_string(cursor, cursor_start, 0, underflow as u8)?; + } + res +} diff --git a/src/lib.rs b/src/lib.rs index 0e93ee9..6bdeb07 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -83,7 +83,7 @@ impl TdfOutput for TdfCsvWriter { let mut writer = std::io::BufWriter::new(std::fs::File::create(path.clone())?); // Write header into file - let heading = tdf::decoders::tdf_fields(&tdf_id).join(","); + let heading = tdf::decoders_csv::tdf_fields(&tdf_id).join(","); writer.write_all(format!("time,{}\n", heading).as_bytes())?; // Touch the count variable in case the decoding fails @@ -98,7 +98,7 @@ impl TdfOutput for TdfCsvWriter { }; // Construct CSV line - let reading = tdf::decoders::tdf_read_into_str(&tdf_id, size, cursor)?; + let reading = tdf::decoders_csv::tdf_read_into_str(&tdf_id, size, cursor)?; let time = match tdf_idx { Some(idx) => { // Use the index directly if provided diff --git a/tdf/src/decoders.rs b/tdf/src/decoders.rs index 2fca6b2..e6cad67 100644 --- a/tdf/src/decoders.rs +++ b/tdf/src/decoders.rs @@ -1,9 +1,6 @@ -use std::io::{Cursor, Read, Result, Error, ErrorKind}; +use std::io::{Cursor, Error, ErrorKind, Read, Result}; -use byteorder::{LittleEndian, BigEndian, ReadBytesExt}; - -pub fn tdf_name(tdf_id: &u16) -> String -{ +pub fn tdf_name(tdf_id: &u16) -> String { match tdf_id { 1 => String::from("ANNOUNCE"), 2 => String::from("BATTERY_STATE"), @@ -69,75 +66,11 @@ pub fn tdf_name(tdf_id: &u16) -> String } } -pub fn tdf_fields(tdf_id: &u16) -> Vec<&'static str> -{ - match tdf_id { - 1 => vec!["application","major","minor","revision","build_num","kv_crc","blocks","uptime","reboots","flags"], - 2 => vec!["voltage_mv","current_ua","soc"], - 3 => vec!["temperature","pressure","humidity"], - 4 => vec!["temperature"], - 5 => vec!["source","shift"], - 6 => vec!["reason","hardware_flags","count","uptime","param_1","param_2","thread"], - 7 => vec!["application","major","minor","revision","build_num","board_crc","kv_crc","blocks","uptime","reboots","flags"], - 8 => vec!["temperature"], - 10 => vec!["x","y","z"], - 11 => vec!["x","y","z"], - 12 => vec!["x","y","z"], - 13 => vec!["x","y","z"], - 14 => vec!["x","y","z"], - 15 => vec!["x","y","z"], - 16 => vec!["x","y","z"], - 17 => vec!["x","y","z"], - 18 => vec!["x","y","z"], - 19 => vec!["latitude","longitude","height","h_acc","v_acc"], - 20 => vec!["itow","year","month","day","hour","min","sec","valid","t_acc","nano","fix_type","flags","flags2","num_sv","lon","lat","height","h_msl","h_acc","v_acc","vel_n","vel_e","vel_d","g_speed","head_mot","s_acc","head_acc","p_dop","flags3","reserved0[0]","reserved0[1]","reserved0[2]","reserved0[3]","head_veh","mag_dec","mag_acc"], - 21 => vec!["mcc","mnc","eci","tac","earfcn","status","tech","rsrp","rsrq"], - 22 => vec!["payload[0]","payload[1]","payload[2]","payload[3]","payload[4]","payload[5]","payload[6]","payload[7]","payload[8]"], - 23 => vec!["count","std_dev"], - 24 => vec!["value"], - 25 => vec!["algorithm_id","algorithm_version","output"], - 26 => vec!["error_id","error_ctx"], - 27 => vec!["enabled"], - 28 => vec!["time_fix","location_fix","num_sv"], - 29 => vec!["type","val","connected"], - 30 => vec!["type","val","rssi"], - 31 => vec!["type","val","throughput"], - 32 => vec!["algorithm_id","algorithm_version","classes"], - 33 => vec!["algorithm_id","algorithm_version","values"], - 34 => vec!["mcc","mnc","eci","tac","earfcn","rsrp","rsrq","earfcn","pci","time_diff","rsrp","rsrq"], - 35 => vec!["val","channel","rsrp"], - 36 => vec!["cosine"], - 37 => vec!["lat","lon","height","h_acc","v_acc","h_speed","h_speed_acc","v_speed","v_speed_acc","head_mot","head_acc","year","month","day","hour","min","sec","ms","p_dop","h_dop","v_dop","t_dop","flags","num_sv"], - 38 => vec!["charge"], - 39 => vec!["infuse_id","rssi"], - 40 => vec!["val"], - 41 => vec!["val"], - 42 => vec!["val"], - 43 => vec!["timestamp","event"], - 44 => vec!["snr","rssi","payload"], - 45 => vec!["payload"], - 46 => vec!["tdf_id","frequency"], - 47 => vec!["tdf_id","period"], - 48 => vec!["bssid","band","channel","iface_mode","link_mode","security","rssi","beacon_interval","twt_capable"], - 49 => vec!["reason"], - 50 => vec!["reason"], - 51 => vec!["num_wifi","num_lte"], - 52 => vec!["frame"], - 53 => vec!["voltage"], - 54 => vec!["soc"], - 55 => vec!["state"], - 56 => vec!["state"], - 57 => vec!["state","duration"], - 58 => vec!["val"], - 59 => vec!["val"], - 60 => vec!["left","right"], - 61 => vec!["key","value"], - _ => vec!["unknown"], - } -} - -fn vla_bytes_remaining(cursor: &mut Cursor<&[u8]>, cursor_start: u64, size: u8) -> Result -{ +pub fn vla_bytes_remaining( + cursor: &mut Cursor<&[u8]>, + cursor_start: u64, + size: u8, +) -> Result { let cursor_current = cursor.position(); let cursor_read = cursor_current - cursor_start; if cursor_read >= size as u64 { @@ -151,8 +84,12 @@ fn vla_bytes_remaining(cursor: &mut Cursor<&[u8]>, cursor_start: u64, size: u8) Ok(bytes_remaining as usize) } -fn tdf_field_read_string(cursor: &mut Cursor<&[u8]>, cursor_start: u64, num: u8, size: u8) -> Result -{ +pub fn tdf_field_read_string( + cursor: &mut Cursor<&[u8]>, + cursor_start: u64, + num: u8, + size: u8, +) -> Result> { let string_length = match num { 0 => vla_bytes_remaining(cursor, cursor_start, size)?, _ => num as usize, @@ -161,521 +98,17 @@ fn tdf_field_read_string(cursor: &mut Cursor<&[u8]>, cursor_start: u64, num: u8, let mut buf = vec![0u8; string_length]; cursor.read_exact(&mut buf)?; - match String::from_utf8(buf) { - Ok(val) => Ok(format!("\"{}\"", val.trim_matches(char::from(0)))), - Err(..) => Ok(String::from("\"\"")) - } + Ok(buf) } -fn tdf_field_read_vla(cursor: &mut Cursor<&[u8]>, cursor_start: u64, size: u8) -> Result -{ +pub fn tdf_field_read_vla( + cursor: &mut Cursor<&[u8]>, + cursor_start: u64, + size: u8, +) -> Result> { let bytes_remaining = vla_bytes_remaining(cursor, cursor_start, size)?; let mut buf = vec![0u8; bytes_remaining]; cursor.read_exact(&mut buf)?; - Ok(format!("{}", hex::encode(buf))) -} - -pub fn tdf_read_into_str(tdf_id: &u16, size: u8, cursor: &mut Cursor<&[u8]>) -> Result -{ - let cursor_start = cursor.position(); - - let res = match tdf_id { - 1 => - Ok(format!( - "0x{:08x},{},{},{},0x{:08x},0x{:08x},{},{},{},0x{:02x}", - cursor.read_u32::()?, - cursor.read_u8()?, - cursor.read_u8()?, - cursor.read_u16::()?, - cursor.read_u32::()?, - cursor.read_u32::()?, - cursor.read_u32::()?, - cursor.read_u32::()?, - cursor.read_u16::()?, - cursor.read_u8()?, - )), - 2 => - Ok(format!( - "{},{},{}", - cursor.read_u32::()?, - cursor.read_i32::()?, - cursor.read_u8()?, - )), - 3 => - Ok(format!( - "{},{},{}", - cursor.read_i32::()? as f64 / 1000.0, - cursor.read_u32::()? as f64 / 1000.0, - cursor.read_u16::()? as f64 / 100.0, - )), - 4 => - Ok(format!( - "{}", - cursor.read_i32::()? as f64 / 1000.0, - )), - 5 => - Ok(format!( - "{},{}", - cursor.read_u8()?, - cursor.read_i32::()? as f64 / 1000000.0, - )), - 6 => - Ok(format!( - "{},0x{:08x},{},{},0x{:08x},0x{:08x},{}", - cursor.read_u8()?, - cursor.read_u32::()?, - cursor.read_u32::()?, - cursor.read_u32::()?, - cursor.read_u32::()?, - cursor.read_u32::()?, - tdf_field_read_string(cursor, cursor_start, 8, size)?, - )), - 7 => - Ok(format!( - "0x{:08x},{},{},{},0x{:08x},0x{:04x},0x{:08x},{},{},{},0x{:02x}", - cursor.read_u32::()?, - cursor.read_u8()?, - cursor.read_u8()?, - cursor.read_u16::()?, - cursor.read_u32::()?, - cursor.read_u16::()?, - cursor.read_u32::()?, - cursor.read_u32::()?, - cursor.read_u32::()?, - cursor.read_u16::()?, - cursor.read_u8()?, - )), - 8 => - Ok(format!( - "{}", - cursor.read_i16::()? as f64 / 100.0, - )), - 10 => - Ok(format!( - "{},{},{}", - cursor.read_i16::()?, - cursor.read_i16::()?, - cursor.read_i16::()?, - )), - 11 => - Ok(format!( - "{},{},{}", - cursor.read_i16::()?, - cursor.read_i16::()?, - cursor.read_i16::()?, - )), - 12 => - Ok(format!( - "{},{},{}", - cursor.read_i16::()?, - cursor.read_i16::()?, - cursor.read_i16::()?, - )), - 13 => - Ok(format!( - "{},{},{}", - cursor.read_i16::()?, - cursor.read_i16::()?, - cursor.read_i16::()?, - )), - 14 => - Ok(format!( - "{},{},{}", - cursor.read_i16::()?, - cursor.read_i16::()?, - cursor.read_i16::()?, - )), - 15 => - Ok(format!( - "{},{},{}", - cursor.read_i16::()?, - cursor.read_i16::()?, - cursor.read_i16::()?, - )), - 16 => - Ok(format!( - "{},{},{}", - cursor.read_i16::()?, - cursor.read_i16::()?, - cursor.read_i16::()?, - )), - 17 => - Ok(format!( - "{},{},{}", - cursor.read_i16::()?, - cursor.read_i16::()?, - cursor.read_i16::()?, - )), - 18 => - Ok(format!( - "{},{},{}", - cursor.read_i16::()?, - cursor.read_i16::()?, - cursor.read_i16::()?, - )), - 19 => - Ok(format!( - "{},{},{},{},{}", - cursor.read_i32::()? as f64 / 10000000.0, - cursor.read_i32::()? as f64 / 10000000.0, - cursor.read_i32::()? as f64 / 1000.0, - cursor.read_i32::()? as f64 / 1000.0, - cursor.read_i32::()? as f64 / 1000.0, - )), - 20 => - Ok(format!( - "{},{},{},{},{},{},{},0x{:02x},{},{},{},0x{:02x},0x{:02x},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},0x{:04x},{},{},{},{},{},{},{}", - cursor.read_u32::()?, - cursor.read_u16::()?, - cursor.read_u8()?, - cursor.read_u8()?, - cursor.read_u8()?, - cursor.read_u8()?, - cursor.read_u8()?, - cursor.read_u8()?, - cursor.read_u32::()?, - cursor.read_i32::()?, - cursor.read_u8()?, - cursor.read_u8()?, - cursor.read_u8()?, - cursor.read_u8()?, - cursor.read_i32::()? as f64 / 10000000.0, - cursor.read_i32::()? as f64 / 10000000.0, - cursor.read_i32::()? as f64 / 1000.0, - cursor.read_i32::()? as f64 / 1000.0, - cursor.read_u32::()? as f64 / 1000.0, - cursor.read_u32::()? as f64 / 1000.0, - cursor.read_i32::()? as f64 / 1000.0, - cursor.read_i32::()? as f64 / 1000.0, - cursor.read_i32::()? as f64 / 1000.0, - cursor.read_i32::()? as f64 / 1000.0, - cursor.read_i32::()? as f64 / 100000.0, - cursor.read_u32::()? as f64 / 1000.0, - cursor.read_u32::()? as f64 / 100000.0, - cursor.read_u16::()? as f64 / 100.0, - cursor.read_u16::()?, - cursor.read_u8()?, - cursor.read_u8()?, - cursor.read_u8()?, - cursor.read_u8()?, - cursor.read_i32::()? as f64 / 100000.0, - cursor.read_i16::()? as f64 / 100.0, - cursor.read_u16::()? as f64 / 100.0, - )), - 21 => - Ok(format!( - "{},{},{},{},{},{},{},{},{}", - cursor.read_u16::()?, - cursor.read_u16::()?, - cursor.read_u32::()?, - cursor.read_u16::()?, - cursor.read_u32::()?, - cursor.read_u8()?, - cursor.read_u8()?, - cursor.read_u8()? as f64 / -1.0, - cursor.read_i8()?, - )), - 22 => - Ok(format!( - "{},{},{},{},{},{},{},{},{}", - cursor.read_u8()?, - cursor.read_u8()?, - cursor.read_u8()?, - cursor.read_u8()?, - cursor.read_u8()?, - cursor.read_u8()?, - cursor.read_u8()?, - cursor.read_u8()?, - cursor.read_u8()?, - )), - 23 => - Ok(format!( - "{},{}", - cursor.read_u32::()?, - cursor.read_u32::()?, - )), - 24 => - Ok(format!( - "{}", - cursor.read_u32::()?, - )), - 25 => - Ok(format!( - "0x{:08x},{},{}", - cursor.read_u32::()?, - cursor.read_u16::()?, - tdf_field_read_vla(cursor, cursor_start, size)?, - )), - 26 => - Ok(format!( - "{},{}", - cursor.read_u32::()?, - cursor.read_u32::()?, - )), - 27 => - Ok(format!( - "{}", - cursor.read_u8()?, - )), - 28 => - Ok(format!( - "{},{},{}", - cursor.read_u16::()?, - cursor.read_u16::()?, - cursor.read_u8()?, - )), - 29 => - Ok(format!( - "{},0x{:012x},{}", - cursor.read_u8()?, - cursor.read_u48::()?, - cursor.read_u8()?, - )), - 30 => - Ok(format!( - "{},0x{:012x},{}", - cursor.read_u8()?, - cursor.read_u48::()?, - cursor.read_i8()?, - )), - 31 => - Ok(format!( - "{},0x{:012x},{}", - cursor.read_u8()?, - cursor.read_u48::()?, - cursor.read_i32::()?, - )), - 32 => - Ok(format!( - "0x{:08x},{},{}", - cursor.read_u32::()?, - cursor.read_u16::()?, - tdf_field_read_vla(cursor, cursor_start, size)?, - )), - 33 => - Ok(format!( - "0x{:08x},{},{}", - cursor.read_u32::()?, - cursor.read_u16::()?, - tdf_field_read_vla(cursor, cursor_start, size)?, - )), - 34 => - Ok(format!( - "{},{},{},{},{},{},{},{},{},{},{},{}", - cursor.read_u16::()?, - cursor.read_u16::()?, - cursor.read_u32::()?, - cursor.read_u16::()?, - cursor.read_u32::()?, - cursor.read_u8()? as f64 / -1.0, - cursor.read_i8()?, - cursor.read_u32::()?, - cursor.read_u16::()?, - cursor.read_u16::()? as f64 / 1000.0, - cursor.read_u8()? as f64 / -1.0, - cursor.read_i8()?, - )), - 35 => - Ok(format!( - "0x{:012x},{},{}", - cursor.read_u48::()?, - cursor.read_u8()?, - cursor.read_i8()?, - )), - 36 => - Ok(format!( - "{}", - cursor.read_f32::()?, - )), - 37 => - Ok(format!( - "{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},0x{:02x},{}", - cursor.read_i32::()? as f64 / 10000000.0, - cursor.read_i32::()? as f64 / 10000000.0, - cursor.read_i32::()? as f64 / 1000.0, - cursor.read_u32::()? as f64 / 1000.0, - cursor.read_u32::()? as f64 / 1000.0, - cursor.read_i32::()? as f64 / 1000.0, - cursor.read_u32::()? as f64 / 1000.0, - cursor.read_i32::()? as f64 / 1000.0, - cursor.read_u32::()? as f64 / 1000.0, - cursor.read_i32::()? as f64 / 100000.0, - cursor.read_u32::()? as f64 / 100000.0, - cursor.read_u16::()?, - cursor.read_u8()?, - cursor.read_u8()?, - cursor.read_u8()?, - cursor.read_u8()?, - cursor.read_u8()?, - cursor.read_u16::()?, - cursor.read_u16::()? as f64 / 100.0, - cursor.read_u16::()? as f64 / 100.0, - cursor.read_u16::()? as f64 / 100.0, - cursor.read_u16::()? as f64 / 100.0, - cursor.read_u8()?, - cursor.read_u8()?, - )), - 38 => - Ok(format!( - "{}", - cursor.read_i32::()?, - )), - 39 => - Ok(format!( - "0x{:016x},{}", - cursor.read_u64::()?, - cursor.read_i8()?, - )), - 40 => - Ok(format!( - "{}", - cursor.read_i8()?, - )), - 41 => - Ok(format!( - "{}", - cursor.read_i16::()?, - )), - 42 => - Ok(format!( - "{}", - cursor.read_i32::()?, - )), - 43 => - Ok(format!( - "{},{}", - cursor.read_u32::()?, - tdf_field_read_string(cursor, cursor_start, 0, size)?, - )), - 44 => - Ok(format!( - "{},{},{}", - cursor.read_i8()?, - cursor.read_i16::()?, - tdf_field_read_vla(cursor, cursor_start, size)?, - )), - 45 => - Ok(format!( - "{}", - tdf_field_read_vla(cursor, cursor_start, size)?, - )), - 46 => - Ok(format!( - "{},{}", - cursor.read_u16::()?, - cursor.read_u32::()?, - )), - 47 => - Ok(format!( - "{},{}", - cursor.read_u16::()?, - cursor.read_u32::()?, - )), - 48 => - Ok(format!( - "0x{:012x},{},{},{},{},{},{},{},{}", - cursor.read_u48::()?, - cursor.read_u8()?, - cursor.read_u8()?, - cursor.read_u8()?, - cursor.read_u8()?, - cursor.read_u8()?, - cursor.read_i8()?, - cursor.read_u16::()?, - cursor.read_u8()?, - )), - 49 => - Ok(format!( - "{}", - cursor.read_u8()?, - )), - 50 => - Ok(format!( - "{}", - cursor.read_u8()?, - )), - 51 => - Ok(format!( - "{},{}", - cursor.read_u8()?, - cursor.read_u8()?, - )), - 52 => - Ok(format!( - "{}", - tdf_field_read_vla(cursor, cursor_start, size)?, - )), - 53 => - Ok(format!( - "{}", - cursor.read_u16::()?, - )), - 54 => - Ok(format!( - "{}", - cursor.read_u8()?, - )), - 55 => - Ok(format!( - "{}", - cursor.read_u8()?, - )), - 56 => - Ok(format!( - "{}", - cursor.read_u8()?, - )), - 57 => - Ok(format!( - "{},{}", - cursor.read_u8()?, - cursor.read_u32::()?, - )), - 58 => - Ok(format!( - "{}", - cursor.read_i16::()?, - )), - 59 => - Ok(format!( - "{}", - cursor.read_i16::()?, - )), - 60 => - Ok(format!( - "{},{}", - cursor.read_i16::()?, - cursor.read_i16::()?, - )), - 61 => - Ok(format!( - "{},{}", - cursor.read_u16::()?, - tdf_field_read_vla(cursor, cursor_start, size)?, - )), - _ => { - let mut buf = vec![0; size as usize]; - cursor.read_exact(&mut buf)?; - Ok(format!("{}", hex::encode(buf))) - } - }; - let cursor_end = cursor.position(); - let cursor_read = cursor_end - cursor_start; - - if (size as u64) < cursor_read { - // Hande read overflow (more data read than specified) - return Result::Err(Error::new( - ErrorKind::InvalidData, - "Read overflow, corrupt data/metadata", - )); - } - - let underflow = size as u64 - cursor_read; - - // Handle read underflow (more data specified than expected) - if underflow > 0 { - tdf_field_read_string(cursor, cursor_start, 0, underflow as u8)?; - } - res + Ok(buf) } diff --git a/tdf/src/decoders_csv.rs b/tdf/src/decoders_csv.rs new file mode 100644 index 0000000..35e7319 --- /dev/null +++ b/tdf/src/decoders_csv.rs @@ -0,0 +1,724 @@ +use std::io::{Cursor, Error, ErrorKind, Read, Result}; + +use byteorder::{BigEndian, LittleEndian, ReadBytesExt}; + +pub fn tdf_fields(tdf_id: &u16) -> Vec<&'static str> { + match tdf_id { + 1 => vec![ + "application", + "major", + "minor", + "revision", + "build_num", + "kv_crc", + "blocks", + "uptime", + "reboots", + "flags", + ], + 2 => vec!["voltage_mv", "current_ua", "soc"], + 3 => vec!["temperature", "pressure", "humidity"], + 4 => vec!["temperature"], + 5 => vec!["source", "shift"], + 6 => vec![ + "reason", + "hardware_flags", + "count", + "uptime", + "param_1", + "param_2", + "thread", + ], + 7 => vec![ + "application", + "major", + "minor", + "revision", + "build_num", + "board_crc", + "kv_crc", + "blocks", + "uptime", + "reboots", + "flags", + ], + 8 => vec!["temperature"], + 10 => vec!["x", "y", "z"], + 11 => vec!["x", "y", "z"], + 12 => vec!["x", "y", "z"], + 13 => vec!["x", "y", "z"], + 14 => vec!["x", "y", "z"], + 15 => vec!["x", "y", "z"], + 16 => vec!["x", "y", "z"], + 17 => vec!["x", "y", "z"], + 18 => vec!["x", "y", "z"], + 19 => vec!["latitude", "longitude", "height", "h_acc", "v_acc"], + 20 => vec![ + "itow", + "year", + "month", + "day", + "hour", + "min", + "sec", + "valid", + "t_acc", + "nano", + "fix_type", + "flags", + "flags2", + "num_sv", + "lon", + "lat", + "height", + "h_msl", + "h_acc", + "v_acc", + "vel_n", + "vel_e", + "vel_d", + "g_speed", + "head_mot", + "s_acc", + "head_acc", + "p_dop", + "flags3", + "reserved0[0]", + "reserved0[1]", + "reserved0[2]", + "reserved0[3]", + "head_veh", + "mag_dec", + "mag_acc", + ], + 21 => vec![ + "mcc", "mnc", "eci", "tac", "earfcn", "status", "tech", "rsrp", "rsrq", + ], + 22 => vec![ + "payload[0]", + "payload[1]", + "payload[2]", + "payload[3]", + "payload[4]", + "payload[5]", + "payload[6]", + "payload[7]", + "payload[8]", + ], + 23 => vec!["count", "std_dev"], + 24 => vec!["value"], + 25 => vec!["algorithm_id", "algorithm_version", "output"], + 26 => vec!["error_id", "error_ctx"], + 27 => vec!["enabled"], + 28 => vec!["time_fix", "location_fix", "num_sv"], + 29 => vec!["type", "val", "connected"], + 30 => vec!["type", "val", "rssi"], + 31 => vec!["type", "val", "throughput"], + 32 => vec!["algorithm_id", "algorithm_version", "classes"], + 33 => vec!["algorithm_id", "algorithm_version", "values"], + 34 => vec![ + "mcc", + "mnc", + "eci", + "tac", + "earfcn", + "rsrp", + "rsrq", + "earfcn", + "pci", + "time_diff", + "rsrp", + "rsrq", + ], + 35 => vec!["val", "channel", "rsrp"], + 36 => vec!["cosine"], + 37 => vec![ + "lat", + "lon", + "height", + "h_acc", + "v_acc", + "h_speed", + "h_speed_acc", + "v_speed", + "v_speed_acc", + "head_mot", + "head_acc", + "year", + "month", + "day", + "hour", + "min", + "sec", + "ms", + "p_dop", + "h_dop", + "v_dop", + "t_dop", + "flags", + "num_sv", + ], + 38 => vec!["charge"], + 39 => vec!["infuse_id", "rssi"], + 40 => vec!["val"], + 41 => vec!["val"], + 42 => vec!["val"], + 43 => vec!["timestamp", "event"], + 44 => vec!["snr", "rssi", "payload"], + 45 => vec!["payload"], + 46 => vec!["tdf_id", "frequency"], + 47 => vec!["tdf_id", "period"], + 48 => vec![ + "bssid", + "band", + "channel", + "iface_mode", + "link_mode", + "security", + "rssi", + "beacon_interval", + "twt_capable", + ], + 49 => vec!["reason"], + 50 => vec!["reason"], + 51 => vec!["num_wifi", "num_lte"], + 52 => vec!["frame"], + 53 => vec!["voltage"], + 54 => vec!["soc"], + 55 => vec!["state"], + 56 => vec!["state"], + 57 => vec!["state", "duration"], + 58 => vec!["val"], + 59 => vec!["val"], + 60 => vec!["left", "right"], + 61 => vec!["key", "value"], + _ => vec!["unknown"], + } +} + +fn tdf_field_read_string_to_str( + cursor: &mut Cursor<&[u8]>, + cursor_start: u64, + num: u8, + size: u8, +) -> Result { + let buf = crate::decoders::tdf_field_read_string(cursor, cursor_start, num, size)?; + + match String::from_utf8(buf) { + Ok(val) => Ok(format!("\"{}\"", val.trim_matches(char::from(0)))), + Err(..) => Ok(String::from("\"\"")), + } +} + +fn tdf_field_read_vla_to_str( + cursor: &mut Cursor<&[u8]>, + cursor_start: u64, + size: u8, +) -> Result { + let buf = crate::decoders::tdf_field_read_vla(cursor, cursor_start, size)?; + + Ok(format!("{}", hex::encode(buf))) +} + +pub fn tdf_read_into_str(tdf_id: &u16, size: u8, cursor: &mut Cursor<&[u8]>) -> Result { + let cursor_start = cursor.position(); + + let res = match tdf_id { + 1 => + Ok(format!( + "0x{:08x},{},{},{},0x{:08x},0x{:08x},{},{},{},0x{:02x}", + cursor.read_u32::()?, + cursor.read_u8()?, + cursor.read_u8()?, + cursor.read_u16::()?, + cursor.read_u32::()?, + cursor.read_u32::()?, + cursor.read_u32::()?, + cursor.read_u32::()?, + cursor.read_u16::()?, + cursor.read_u8()?, + )), + 2 => + Ok(format!( + "{},{},{}", + cursor.read_u32::()?, + cursor.read_i32::()?, + cursor.read_u8()?, + )), + 3 => + Ok(format!( + "{},{},{}", + cursor.read_i32::()? as f64 / 1000.0, + cursor.read_u32::()? as f64 / 1000.0, + cursor.read_u16::()? as f64 / 100.0, + )), + 4 => + Ok(format!( + "{}", + cursor.read_i32::()? as f64 / 1000.0, + )), + 5 => + Ok(format!( + "{},{}", + cursor.read_u8()?, + cursor.read_i32::()? as f64 / 1000000.0, + )), + 6 => + Ok(format!( + "{},0x{:08x},{},{},0x{:08x},0x{:08x},{}", + cursor.read_u8()?, + cursor.read_u32::()?, + cursor.read_u32::()?, + cursor.read_u32::()?, + cursor.read_u32::()?, + cursor.read_u32::()?, + tdf_field_read_string_to_str(cursor, cursor_start, 8, size)?, + )), + 7 => + Ok(format!( + "0x{:08x},{},{},{},0x{:08x},0x{:04x},0x{:08x},{},{},{},0x{:02x}", + cursor.read_u32::()?, + cursor.read_u8()?, + cursor.read_u8()?, + cursor.read_u16::()?, + cursor.read_u32::()?, + cursor.read_u16::()?, + cursor.read_u32::()?, + cursor.read_u32::()?, + cursor.read_u32::()?, + cursor.read_u16::()?, + cursor.read_u8()?, + )), + 8 => + Ok(format!( + "{}", + cursor.read_i16::()? as f64 / 100.0, + )), + 10 => + Ok(format!( + "{},{},{}", + cursor.read_i16::()?, + cursor.read_i16::()?, + cursor.read_i16::()?, + )), + 11 => + Ok(format!( + "{},{},{}", + cursor.read_i16::()?, + cursor.read_i16::()?, + cursor.read_i16::()?, + )), + 12 => + Ok(format!( + "{},{},{}", + cursor.read_i16::()?, + cursor.read_i16::()?, + cursor.read_i16::()?, + )), + 13 => + Ok(format!( + "{},{},{}", + cursor.read_i16::()?, + cursor.read_i16::()?, + cursor.read_i16::()?, + )), + 14 => + Ok(format!( + "{},{},{}", + cursor.read_i16::()?, + cursor.read_i16::()?, + cursor.read_i16::()?, + )), + 15 => + Ok(format!( + "{},{},{}", + cursor.read_i16::()?, + cursor.read_i16::()?, + cursor.read_i16::()?, + )), + 16 => + Ok(format!( + "{},{},{}", + cursor.read_i16::()?, + cursor.read_i16::()?, + cursor.read_i16::()?, + )), + 17 => + Ok(format!( + "{},{},{}", + cursor.read_i16::()?, + cursor.read_i16::()?, + cursor.read_i16::()?, + )), + 18 => + Ok(format!( + "{},{},{}", + cursor.read_i16::()?, + cursor.read_i16::()?, + cursor.read_i16::()?, + )), + 19 => + Ok(format!( + "{},{},{},{},{}", + cursor.read_i32::()? as f64 / 10000000.0, + cursor.read_i32::()? as f64 / 10000000.0, + cursor.read_i32::()? as f64 / 1000.0, + cursor.read_i32::()? as f64 / 1000.0, + cursor.read_i32::()? as f64 / 1000.0, + )), + 20 => + Ok(format!( + "{},{},{},{},{},{},{},0x{:02x},{},{},{},0x{:02x},0x{:02x},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},0x{:04x},{},{},{},{},{},{},{}", + cursor.read_u32::()?, + cursor.read_u16::()?, + cursor.read_u8()?, + cursor.read_u8()?, + cursor.read_u8()?, + cursor.read_u8()?, + cursor.read_u8()?, + cursor.read_u8()?, + cursor.read_u32::()?, + cursor.read_i32::()?, + cursor.read_u8()?, + cursor.read_u8()?, + cursor.read_u8()?, + cursor.read_u8()?, + cursor.read_i32::()? as f64 / 10000000.0, + cursor.read_i32::()? as f64 / 10000000.0, + cursor.read_i32::()? as f64 / 1000.0, + cursor.read_i32::()? as f64 / 1000.0, + cursor.read_u32::()? as f64 / 1000.0, + cursor.read_u32::()? as f64 / 1000.0, + cursor.read_i32::()? as f64 / 1000.0, + cursor.read_i32::()? as f64 / 1000.0, + cursor.read_i32::()? as f64 / 1000.0, + cursor.read_i32::()? as f64 / 1000.0, + cursor.read_i32::()? as f64 / 100000.0, + cursor.read_u32::()? as f64 / 1000.0, + cursor.read_u32::()? as f64 / 100000.0, + cursor.read_u16::()? as f64 / 100.0, + cursor.read_u16::()?, + cursor.read_u8()?, + cursor.read_u8()?, + cursor.read_u8()?, + cursor.read_u8()?, + cursor.read_i32::()? as f64 / 100000.0, + cursor.read_i16::()? as f64 / 100.0, + cursor.read_u16::()? as f64 / 100.0, + )), + 21 => + Ok(format!( + "{},{},{},{},{},{},{},{},{}", + cursor.read_u16::()?, + cursor.read_u16::()?, + cursor.read_u32::()?, + cursor.read_u16::()?, + cursor.read_u32::()?, + cursor.read_u8()?, + cursor.read_u8()?, + cursor.read_u8()? as f64 / -1.0, + cursor.read_i8()?, + )), + 22 => + Ok(format!( + "{},{},{},{},{},{},{},{},{}", + cursor.read_u8()?, + cursor.read_u8()?, + cursor.read_u8()?, + cursor.read_u8()?, + cursor.read_u8()?, + cursor.read_u8()?, + cursor.read_u8()?, + cursor.read_u8()?, + cursor.read_u8()?, + )), + 23 => + Ok(format!( + "{},{}", + cursor.read_u32::()?, + cursor.read_u32::()?, + )), + 24 => + Ok(format!( + "{}", + cursor.read_u32::()?, + )), + 25 => + Ok(format!( + "0x{:08x},{},{}", + cursor.read_u32::()?, + cursor.read_u16::()?, + tdf_field_read_vla_to_str(cursor, cursor_start, size)?, + )), + 26 => + Ok(format!( + "{},{}", + cursor.read_u32::()?, + cursor.read_u32::()?, + )), + 27 => + Ok(format!( + "{}", + cursor.read_u8()?, + )), + 28 => + Ok(format!( + "{},{},{}", + cursor.read_u16::()?, + cursor.read_u16::()?, + cursor.read_u8()?, + )), + 29 => + Ok(format!( + "{},0x{:012x},{}", + cursor.read_u8()?, + cursor.read_u48::()?, + cursor.read_u8()?, + )), + 30 => + Ok(format!( + "{},0x{:012x},{}", + cursor.read_u8()?, + cursor.read_u48::()?, + cursor.read_i8()?, + )), + 31 => + Ok(format!( + "{},0x{:012x},{}", + cursor.read_u8()?, + cursor.read_u48::()?, + cursor.read_i32::()?, + )), + 32 => + Ok(format!( + "0x{:08x},{},{}", + cursor.read_u32::()?, + cursor.read_u16::()?, + tdf_field_read_vla_to_str(cursor, cursor_start, size)?, + )), + 33 => + Ok(format!( + "0x{:08x},{},{}", + cursor.read_u32::()?, + cursor.read_u16::()?, + tdf_field_read_vla_to_str(cursor, cursor_start, size)?, + )), + 34 => + Ok(format!( + "{},{},{},{},{},{},{},{},{},{},{},{}", + cursor.read_u16::()?, + cursor.read_u16::()?, + cursor.read_u32::()?, + cursor.read_u16::()?, + cursor.read_u32::()?, + cursor.read_u8()? as f64 / -1.0, + cursor.read_i8()?, + cursor.read_u32::()?, + cursor.read_u16::()?, + cursor.read_u16::()? as f64 / 1000.0, + cursor.read_u8()? as f64 / -1.0, + cursor.read_i8()?, + )), + 35 => + Ok(format!( + "0x{:012x},{},{}", + cursor.read_u48::()?, + cursor.read_u8()?, + cursor.read_i8()?, + )), + 36 => + Ok(format!( + "{}", + cursor.read_f32::()?, + )), + 37 => + Ok(format!( + "{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},0x{:02x},{}", + cursor.read_i32::()? as f64 / 10000000.0, + cursor.read_i32::()? as f64 / 10000000.0, + cursor.read_i32::()? as f64 / 1000.0, + cursor.read_u32::()? as f64 / 1000.0, + cursor.read_u32::()? as f64 / 1000.0, + cursor.read_i32::()? as f64 / 1000.0, + cursor.read_u32::()? as f64 / 1000.0, + cursor.read_i32::()? as f64 / 1000.0, + cursor.read_u32::()? as f64 / 1000.0, + cursor.read_i32::()? as f64 / 100000.0, + cursor.read_u32::()? as f64 / 100000.0, + cursor.read_u16::()?, + cursor.read_u8()?, + cursor.read_u8()?, + cursor.read_u8()?, + cursor.read_u8()?, + cursor.read_u8()?, + cursor.read_u16::()?, + cursor.read_u16::()? as f64 / 100.0, + cursor.read_u16::()? as f64 / 100.0, + cursor.read_u16::()? as f64 / 100.0, + cursor.read_u16::()? as f64 / 100.0, + cursor.read_u8()?, + cursor.read_u8()?, + )), + 38 => + Ok(format!( + "{}", + cursor.read_i32::()?, + )), + 39 => + Ok(format!( + "0x{:016x},{}", + cursor.read_u64::()?, + cursor.read_i8()?, + )), + 40 => + Ok(format!( + "{}", + cursor.read_i8()?, + )), + 41 => + Ok(format!( + "{}", + cursor.read_i16::()?, + )), + 42 => + Ok(format!( + "{}", + cursor.read_i32::()?, + )), + 43 => + Ok(format!( + "{},{}", + cursor.read_u32::()?, + tdf_field_read_string_to_str(cursor, cursor_start, 0, size)?, + )), + 44 => + Ok(format!( + "{},{},{}", + cursor.read_i8()?, + cursor.read_i16::()?, + tdf_field_read_vla_to_str(cursor, cursor_start, size)?, + )), + 45 => + Ok(format!( + "{}", + tdf_field_read_vla_to_str(cursor, cursor_start, size)?, + )), + 46 => + Ok(format!( + "{},{}", + cursor.read_u16::()?, + cursor.read_u32::()?, + )), + 47 => + Ok(format!( + "{},{}", + cursor.read_u16::()?, + cursor.read_u32::()?, + )), + 48 => + Ok(format!( + "0x{:012x},{},{},{},{},{},{},{},{}", + cursor.read_u48::()?, + cursor.read_u8()?, + cursor.read_u8()?, + cursor.read_u8()?, + cursor.read_u8()?, + cursor.read_u8()?, + cursor.read_i8()?, + cursor.read_u16::()?, + cursor.read_u8()?, + )), + 49 => + Ok(format!( + "{}", + cursor.read_u8()?, + )), + 50 => + Ok(format!( + "{}", + cursor.read_u8()?, + )), + 51 => + Ok(format!( + "{},{}", + cursor.read_u8()?, + cursor.read_u8()?, + )), + 52 => + Ok(format!( + "{}", + tdf_field_read_vla_to_str(cursor, cursor_start, size)?, + )), + 53 => + Ok(format!( + "{}", + cursor.read_u16::()?, + )), + 54 => + Ok(format!( + "{}", + cursor.read_u8()?, + )), + 55 => + Ok(format!( + "{}", + cursor.read_u8()?, + )), + 56 => + Ok(format!( + "{}", + cursor.read_u8()?, + )), + 57 => + Ok(format!( + "{},{}", + cursor.read_u8()?, + cursor.read_u32::()?, + )), + 58 => + Ok(format!( + "{}", + cursor.read_i16::()?, + )), + 59 => + Ok(format!( + "{}", + cursor.read_i16::()?, + )), + 60 => + Ok(format!( + "{},{}", + cursor.read_i16::()?, + cursor.read_i16::()?, + )), + 61 => + Ok(format!( + "{},{}", + cursor.read_u16::()?, + tdf_field_read_vla_to_str(cursor, cursor_start, size)?, + )), + _ => { + let mut buf = vec![0; size as usize]; + cursor.read_exact(&mut buf)?; + Ok(format!("{}", hex::encode(buf))) + } + }; + let cursor_end = cursor.position(); + let cursor_read = cursor_end - cursor_start; + + if (size as u64) < cursor_read { + // Hande read overflow (more data read than specified) + return Result::Err(Error::new( + ErrorKind::InvalidData, + "Read overflow, corrupt data/metadata", + )); + } + + let underflow = size as u64 - cursor_read; + + // Handle read underflow (more data specified than expected) + if underflow > 0 { + crate::decoders::tdf_field_read_string(cursor, cursor_start, 0, underflow as u8)?; + } + res +} diff --git a/tdf/src/lib.rs b/tdf/src/lib.rs index 6573324..66775fe 100644 --- a/tdf/src/lib.rs +++ b/tdf/src/lib.rs @@ -4,6 +4,7 @@ use num::{cast::AsPrimitive, traits::WrappingAdd}; use std::io::{Cursor, ErrorKind, Read}; pub mod decoders; +pub mod decoders_csv; pub mod time; const TDF_TIME_MASK: u16 = 0xC000;