use std::{
collections::VecDeque,
io::{self, Read},
time::Duration,
};
use circular_buffer::CircularBuffer;
use serialport::{Parity, StopBits};
use rawzeo::DataType;
fn main() {
let port_name = "/dev/ttyUSB0";
let baud_rate: u32 = 38400;
let port = serialport::new(port_name, baud_rate)
.parity(Parity::None)
.stop_bits(StopBits::One)
.timeout(Duration::from_millis(10))
.open();
match port {
Ok(mut port) => {
let mut buffer = [0; 512];
let mut ring = CircularBuffer::<512, u8>::new();
let mut prev_seqnum = None;
let mut bytes_received;
let mut result = Ok(None);
println!("Receiving data on {} at {} baud:", &port_name, &baud_rate);
loop {
bytes_received = 0;
match port.read(&mut buffer) {
Ok(n) => {
bytes_received = n;
println!("EXTENDING ring with {n} bytes");
ring.extend_from_slice(&buffer[..n]);
result = parse::<512>(&mut prev_seqnum, &mut ring);
}
Err(ref e) if e.kind() == io::ErrorKind::TimedOut => (),
Err(e) => eprintln!("{:?}", e),
};
if bytes_received > 0 {
if let Ok(Some(ref parsed_msg)) = result {
println!("PARSED: {parsed_msg:?}");
}
}
}
}
Err(e) => {
eprintln!("Failed to open \"{}\". Error: {}", port_name, e);
::std::process::exit(1);
}
}
}
#[rustfmt::skip]
fn print_bytes<E: ExactSizeIterator>(bytes: E) where <E as Iterator>::Item: core::fmt::UpperHex {
print!("[{} B]: ", bytes.len());
for b in bytes { print!("{b:02X} "); }
println!();
}
#[derive(Clone, Debug)]
#[allow(dead_code)] pub struct ParsedMessage {
time_full: u32,
tt_ss: u16,
version: u32,
ty: DataType,
data: VecDeque<u8>,
}
fn parse<const LEN: usize>(
prev_seqnum: &mut Option<u8>,
ring: &mut CircularBuffer<LEN, u8>,
) -> Result<Option<ParsedMessage>, &'static str> {
let mut tt_ss = 0_u16;
let mut datatype = DataType::Invalid(255);
let mut datavec = VecDeque::new();
let mut zeo_version = 0_u32;
let mut zeo_time: u32;
let mut zeo_time_full = 0_u32;
let mut while_counter = 0;
while ring.len() > 15 {
print!("\n» PARSE_{} ", while_counter);
print_bytes(ring.iter());
let mut start = [0; 2];
'inner: loop {
start.swap(0, 1);
start[1] = ring.pop_front().unwrap();
if &start == b"A4" {
break 'inner;
}
}
if ring.len() < 14 {
println!("> (not enough bytes left: {} )", ring.len());
ring.push_front(0x34); ring.push_front(0x41); return Ok(None);
}
let cksum = ring.pop_front().unwrap();
println!("> checksum: 0x{cksum:02X} ({cksum})");
let dl = u16::from_le_bytes([ring.pop_front().unwrap(), ring.pop_front().unwrap()]);
let inv_dl = u16::from_le_bytes([ring.pop_front().unwrap(), ring.pop_front().unwrap()]);
println!("> dl:{dl} inv:{inv_dl}→(inv:{})", !inv_dl);
if dl != !inv_dl {
return Err("Invalid message length.");
}
let tt_lb = ring.pop_front().unwrap();
tt_ss = u16::from_le_bytes([ring.pop_front().unwrap(), ring.pop_front().unwrap()]);
let tt_fss = (tt_ss.saturating_sub(1)) as f32 / 15.0;
println!("> tt_lb: 0x{tt_lb:02X} ({tt_lb}), tt_ss:({tt_ss})({tt_fss:.02})");
let seqnum = ring.pop_front().unwrap();
println!("> seqnum: {seqnum}");
if let Some(pseq) = prev_seqnum {
let prev_seq1 = pseq.wrapping_add(1);
if prev_seq1 != seqnum {
println!["we've lost {} sequence(s)!", seqnum - prev_seq1];
}
}
*prev_seqnum = Some(seqnum);
let dtype = ring.pop_front().unwrap();
datatype = DataType::from(dtype);
println!("> datatype: {datatype}");
let datalen = dl - 1;
if ring.len() < 4 {
println!(
"> less than 4 data bytes!: {} {}",
ring.len(),
"=".repeat(20)
);
}
datavec = VecDeque::<u8>::with_capacity(datalen as usize);
for _ in 0..datalen {
if let Some(byte) = ring.pop_front() {
datavec.push_back(byte);
} else {
println!(">> warning, not enough data!!");
}
}
print!("> DATA: ");
print_bytes(datavec.iter());
if (dtype as u32 + datavec.iter().map(|b| *b as u32).sum::<u32>()) % 256 != cksum as u32 {
return Err("Invalid checksum.");
}
if let DataType::Invalid(_b) = datatype {
return Err("Bad datatype: {{b:02X}}"); }
if datatype == DataType::ZeoTimestamp {
zeo_time = u32::from_le_bytes([
datavec.pop_front().unwrap(),
datavec.pop_front().unwrap(),
datavec.pop_front().unwrap(),
datavec.pop_front().unwrap_or(0), ]);
println!("> zeo_time: {}", zeo_time);
if zeo_time & 0xFF == tt_lb as u32 {
zeo_time_full = zeo_time;
println!(">> tt CHECK A")
} else if (zeo_time.saturating_sub(1)) & 0xFF == tt_lb as u32 {
zeo_time_full = zeo_time.saturating_sub(1);
println!(">> tt CHECK B {}", "=".repeat(10))
} else if (zeo_time.saturating_add(1)) & 0xFF == tt_lb as u32 {
zeo_time_full = zeo_time.saturating_add(1);
println!(">> tt CHECK C {}", "=".repeat(10))
} else {
zeo_time_full = zeo_time;
println!(">> tt CHECK D {}", "=".repeat(10))
}
} else if datatype == DataType::Version {
zeo_version = u32::from_le_bytes([
datavec.pop_front().unwrap(),
datavec.pop_front().unwrap(),
datavec.pop_front().unwrap(),
datavec.pop_front().unwrap_or(0),
]);
println!("> zeo_version: {}", zeo_version);
}
println!("> zeo_time_full: {zeo_time_full} + {tt_ss} ({tt_fss:.02})");
while_counter += 1;
}
Ok(Some(ParsedMessage {
time_full: zeo_time_full,
tt_ss,
version: zeo_version,
ty: datatype,
data: datavec,
}))
}