use crate::types::*;
pub struct Parser {
state: State,
payload: Vec<u8>,
plength: u8,
checksum: u8,
}
enum State {
NoSync,
FirstSync,
ReadLength,
ReadPayload,
}
impl Parser {
pub fn new() -> Self {
Parser {
state: State::NoSync,
payload: Vec::with_capacity(256),
plength: 0,
checksum: 0,
}
}
pub fn parse_byte(&mut self, byte: u8) -> Option<Vec<Packet>> {
match self.state {
State::NoSync => {
if byte == SYNC_BYTE {
self.state = State::FirstSync;
}
None
}
State::FirstSync => {
if byte == SYNC_BYTE {
self.state = State::ReadLength;
} else {
self.state = State::NoSync;
}
None
}
State::ReadLength => {
if byte > MAX_PAYLOAD_LENGTH {
self.state = State::NoSync;
None
} else {
self.plength = byte;
self.payload.clear();
self.checksum = 0;
self.state = State::ReadPayload;
None
}
}
State::ReadPayload => {
if self.payload.len() < self.plength as usize {
self.payload.push(byte);
self.checksum = self.checksum.wrapping_add(byte);
None
} else {
self.state = State::NoSync;
let expected = !self.checksum;
if byte == expected {
Some(Self::decode_payload(&self.payload))
} else {
log::debug!("Checksum mismatch: got 0x{:02X}, expected 0x{:02X}", byte, expected);
None
}
}
}
}
}
pub fn parse(&mut self, data: &[u8]) -> Vec<Packet> {
let mut packets = Vec::new();
for &byte in data {
if let Some(mut p) = self.parse_byte(byte) {
packets.append(&mut p);
}
}
packets
}
fn decode_payload(payload: &[u8]) -> Vec<Packet> {
let mut packets = Vec::new();
let mut i = 0;
while i < payload.len() {
let code = payload[i];
i += 1;
if code >= 0x80 {
if i >= payload.len() { break; }
let len = payload[i] as usize;
i += 1;
if i + len > payload.len() { break; }
let data = &payload[i..i + len];
i += len;
match code {
CODE_RAW_VALUE => {
if let Some(v) = decode_raw_value(data) {
packets.push(Packet::RawValue(v));
}
}
CODE_ASIC_EEG => {
if let Some(eeg) = decode_asic_eeg(data) {
packets.push(Packet::AsicEeg(eeg));
}
}
CODE_HEADSET_CONNECTED if len >= 2 => {
let id = (data[0] as u16) << 8 | data[1] as u16;
packets.push(Packet::HeadsetConnected(id));
}
CODE_HEADSET_NOT_FOUND => {
packets.push(Packet::HeadsetNotFound);
}
CODE_HEADSET_DISCONNECTED => {
packets.push(Packet::HeadsetDisconnected);
}
CODE_STANDBY => {
packets.push(Packet::Standby);
}
CODE_REQUEST_DENIED => {
packets.push(Packet::RequestDenied);
}
_ => {
log::debug!("Unknown multi-byte code: 0x{:02X}", code);
}
}
} else {
if i >= payload.len() { break; }
let value = payload[i];
i += 1;
match code {
CODE_POOR_SIGNAL => packets.push(Packet::PoorSignal(value)),
CODE_ATTENTION => packets.push(Packet::Attention(value)),
CODE_MEDITATION => packets.push(Packet::Meditation(value)),
CODE_BLINK => packets.push(Packet::Blink(value)),
_ => {
log::debug!("Unknown single-byte code: 0x{:02X}", code);
}
}
}
}
packets
}
}
#[cfg(test)]
mod tests {
use super::*;
fn make_packet(payload: &[u8]) -> Vec<u8> {
let checksum = !payload.iter().fold(0u8, |a, &b| a.wrapping_add(b));
let mut pkt = vec![0xAA, 0xAA, payload.len() as u8];
pkt.extend_from_slice(payload);
pkt.push(checksum);
pkt
}
#[test]
fn test_parse_attention() {
let mut parser = Parser::new();
let data = make_packet(&[CODE_ATTENTION, 75]);
let packets = parser.parse(&data);
assert_eq!(packets.len(), 1);
match &packets[0] {
Packet::Attention(v) => assert_eq!(*v, 75),
other => panic!("Expected Attention, got {:?}", other),
}
}
#[test]
fn test_parse_meditation() {
let mut parser = Parser::new();
let data = make_packet(&[CODE_MEDITATION, 50]);
let packets = parser.parse(&data);
assert_eq!(packets.len(), 1);
match &packets[0] { Packet::Meditation(v) => assert_eq!(*v, 50), _ => panic!() }
}
#[test]
fn test_parse_poor_signal() {
let mut parser = Parser::new();
let data = make_packet(&[CODE_POOR_SIGNAL, 200]);
let packets = parser.parse(&data);
match &packets[0] { Packet::PoorSignal(v) => assert_eq!(*v, 200), _ => panic!() }
}
#[test]
fn test_parse_blink() {
let mut parser = Parser::new();
let data = make_packet(&[CODE_BLINK, 128]);
let packets = parser.parse(&data);
match &packets[0] { Packet::Blink(v) => assert_eq!(*v, 128), _ => panic!() }
}
#[test]
fn test_parse_raw_value() {
let mut parser = Parser::new();
let data = make_packet(&[CODE_RAW_VALUE, 0x02, 0x01, 0x00]);
let packets = parser.parse(&data);
match &packets[0] { Packet::RawValue(v) => assert_eq!(*v, 256), _ => panic!() }
}
#[test]
fn test_parse_raw_value_negative() {
let mut parser = Parser::new();
let data = make_packet(&[CODE_RAW_VALUE, 0x02, 0xFF, 0xFE]);
let packets = parser.parse(&data);
match &packets[0] { Packet::RawValue(v) => assert_eq!(*v, -2), _ => panic!() }
}
#[test]
fn test_parse_asic_eeg() {
let mut parser = Parser::new();
let mut payload = vec![CODE_ASIC_EEG, 24]; for i in 0..8u32 {
let v = (i + 1) * 1000;
payload.push((v >> 16) as u8);
payload.push((v >> 8) as u8);
payload.push(v as u8);
}
let data = make_packet(&payload);
let packets = parser.parse(&data);
match &packets[0] {
Packet::AsicEeg(eeg) => {
assert_eq!(eeg.delta, 1000);
assert_eq!(eeg.theta, 2000);
assert_eq!(eeg.low_alpha, 3000);
assert_eq!(eeg.mid_gamma, 8000);
}
_ => panic!()
}
}
#[test]
fn test_parse_headset_connected() {
let mut parser = Parser::new();
let data = make_packet(&[CODE_HEADSET_CONNECTED, 0x02, 0xAB, 0xCD]);
let packets = parser.parse(&data);
match &packets[0] { Packet::HeadsetConnected(id) => assert_eq!(*id, 0xABCD), _ => panic!() }
}
#[test]
fn test_parse_standby() {
let mut parser = Parser::new();
let data = make_packet(&[CODE_STANDBY, 0x00]);
let packets = parser.parse(&data);
assert!(matches!(&packets[0], Packet::Standby));
}
#[test]
fn test_bad_checksum_rejected() {
let mut parser = Parser::new();
let mut data = make_packet(&[CODE_ATTENTION, 75]);
*data.last_mut().unwrap() ^= 0xFF; let packets = parser.parse(&data);
assert!(packets.is_empty());
}
#[test]
fn test_multiple_packets_in_stream() {
let mut parser = Parser::new();
let mut data = make_packet(&[CODE_ATTENTION, 80]);
data.extend_from_slice(&make_packet(&[CODE_MEDITATION, 60]));
let packets = parser.parse(&data);
assert_eq!(packets.len(), 2);
}
#[test]
fn test_noise_before_sync() {
let mut parser = Parser::new();
let mut data = vec![0x00, 0xFF, 0x42]; data.extend_from_slice(&make_packet(&[CODE_ATTENTION, 90]));
let packets = parser.parse(&data);
assert_eq!(packets.len(), 1);
match &packets[0] { Packet::Attention(v) => assert_eq!(*v, 90), _ => panic!() }
}
}