-
Notifications
You must be signed in to change notification settings - Fork 31
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Use own proc macro to generate code to deal with packets (#7)
* Use own proc macro to generate code to deal with packets Features: - removes unnecessary dependicies - more type safe code - faster parser: 3x fixes #1 * make all names in ubx_packets starts with Ubx for uniformity * export packet size as constant Usefull for SPI, where we need provide read buffer simultaneously with write buffer * feature: make possible to print (+validate) extension field in MonVer * feature: new packages: Nav-Dop, Nav-Sol, AckNak * feature: nice method to check if ack for certain message * fix: generted code for handling i8 field in ubx_packet_recv * cleanups/typo fixes plus formating * fix several clippy warnings * parser/refactoring: simplify work with &data[pos..] * parser_tests/refactroing: split big test into small one * make possible to reuse ublox dump test * feature: automatically calculate max packet size for parser * refactoring: rename CfgMsg(3|8) -> CfgMsg(Single|All)Port(s) * feature: Debug implementation for FixStatusInfo * ublox_derive/src/input refactoring: use "use syn::Error" * ublox_derive: check that rest_handling not already defined * refactoring: simplify generate_send_code_for_packet * fix comment grammar * ublox_derive/src/input: improve error message * fix tests * refactoring: usage of fold, plus more appropriate names
- Loading branch information
Showing
21 changed files
with
3,847 additions
and
1,497 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,25 +1,68 @@ | ||
use std::convert; | ||
use std::io; | ||
use std::fmt; | ||
|
||
#[derive(Debug)] | ||
pub enum Error { | ||
InvalidChecksum, | ||
UnexpectedPacket, | ||
TimedOutWaitingForAck(u8, u8), | ||
IoError(io::Error), | ||
BincodeError(bincode::Error), | ||
pub enum MemWriterError<E> | ||
where | ||
E: std::error::Error, | ||
{ | ||
NotEnoughMem, | ||
Custom(E), | ||
} | ||
|
||
impl convert::From<io::Error> for Error { | ||
fn from(error: io::Error) -> Self { | ||
Error::IoError(error) | ||
impl<E> fmt::Display for MemWriterError<E> | ||
where | ||
E: std::error::Error, | ||
{ | ||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | ||
match self { | ||
MemWriterError::NotEnoughMem => f.write_str("Not enough memory error"), | ||
MemWriterError::Custom(e) => write!(f, "MemWriterError: {}", e), | ||
} | ||
} | ||
} | ||
|
||
impl convert::From<bincode::Error> for Error { | ||
fn from(error: bincode::Error) -> Self { | ||
Error::BincodeError(error) | ||
impl<E> std::error::Error for MemWriterError<E> where E: std::error::Error {} | ||
|
||
/// Error that possible during packets parsing | ||
#[derive(Debug, PartialEq)] | ||
pub enum ParserError { | ||
InvalidChecksum { | ||
expect: u16, | ||
got: u16, | ||
}, | ||
InvalidField { | ||
packet: &'static str, | ||
field: &'static str, | ||
}, | ||
InvalidPacketLen { | ||
packet: &'static str, | ||
expect: usize, | ||
got: usize, | ||
}, | ||
} | ||
|
||
impl fmt::Display for ParserError { | ||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | ||
match self { | ||
ParserError::InvalidChecksum { expect, got } => write!( | ||
f, | ||
"Not valid packet's checksum, expect {:x}, got {:x}", | ||
expect, got | ||
), | ||
ParserError::InvalidField { packet, field } => { | ||
write!(f, "Invalid field {} of packet {}", field, packet) | ||
} | ||
ParserError::InvalidPacketLen { | ||
packet, | ||
expect, | ||
got, | ||
} => write!( | ||
f, | ||
"Invalid packet({}) length, expect {}, got {}", | ||
packet, expect, got | ||
), | ||
} | ||
} | ||
} | ||
|
||
pub type Result<T> = std::result::Result<T, Error>; | ||
impl std::error::Error for ParserError {} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,122 @@ | ||
use crate::{ | ||
error::ParserError, | ||
ubx_packets::{ | ||
match_packet, ubx_checksum, PacketRef, MAX_PAYLOAD_LEN, SYNC_CHAR_1, SYNC_CHAR_2, | ||
}, | ||
}; | ||
|
||
/// Streaming parser for UBX protocol with buffer | ||
#[derive(Default)] | ||
pub struct Parser { | ||
buf: Vec<u8>, | ||
} | ||
|
||
impl Parser { | ||
pub fn is_buffer_empty(&self) -> bool { | ||
self.buf.is_empty() | ||
} | ||
|
||
pub fn buffer_len(&self) -> usize { | ||
self.buf.len() | ||
} | ||
|
||
pub fn consume(&mut self, new_data: &[u8]) -> ParserIter { | ||
match self | ||
.buf | ||
.iter() | ||
.chain(new_data.iter()) | ||
.position(|x| *x == SYNC_CHAR_1) | ||
{ | ||
Some(mut off) => { | ||
if off >= self.buf.len() { | ||
off -= self.buf.len(); | ||
self.buf.clear(); | ||
self.buf.extend_from_slice(&new_data[off..]); | ||
off = 0; | ||
} else { | ||
self.buf.extend_from_slice(new_data); | ||
} | ||
ParserIter { | ||
buf: &mut self.buf, | ||
off, | ||
} | ||
} | ||
None => { | ||
self.buf.clear(); | ||
ParserIter { | ||
buf: &mut self.buf, | ||
off: 0, | ||
} | ||
} | ||
} | ||
} | ||
} | ||
|
||
/// Iterator over data stored in `Parser` buffer | ||
pub struct ParserIter<'a> { | ||
buf: &'a mut Vec<u8>, | ||
off: usize, | ||
} | ||
|
||
impl<'a> Drop for ParserIter<'a> { | ||
fn drop(&mut self) { | ||
if self.off <= self.buf.len() { | ||
self.buf.drain(0..self.off); | ||
} | ||
} | ||
} | ||
|
||
impl<'a> ParserIter<'a> { | ||
/// Analog of `core::iter::Iterator::next`, should be switched to | ||
/// trait implmentation after merge of https://github.com/rust-lang/rust/issues/44265 | ||
pub fn next(&mut self) -> Option<Result<PacketRef, ParserError>> { | ||
while self.off < self.buf.len() { | ||
let data = &self.buf[self.off..]; | ||
let pos = data.iter().position(|x| *x == SYNC_CHAR_1)?; | ||
let maybe_pack = &data[pos..]; | ||
|
||
if maybe_pack.len() <= 1 { | ||
return None; | ||
} | ||
if maybe_pack[1] != SYNC_CHAR_2 { | ||
self.off += pos + 2; | ||
continue; | ||
} | ||
|
||
if maybe_pack.len() <= 5 { | ||
return None; | ||
} | ||
|
||
let pack_len: usize = u16::from_le_bytes([maybe_pack[4], maybe_pack[5]]).into(); | ||
if pack_len > usize::from(MAX_PAYLOAD_LEN) { | ||
self.off += pos + 2; | ||
continue; | ||
} | ||
if (pack_len + 6 + 2) > maybe_pack.len() { | ||
return None; | ||
} | ||
let (ck_a, ck_b) = ubx_checksum(&maybe_pack[2..(4 + pack_len + 2)]); | ||
|
||
let (expect_ck_a, expect_ck_b) = | ||
(maybe_pack[6 + pack_len], maybe_pack[6 + pack_len + 1]); | ||
if (ck_a, ck_b) != (expect_ck_a, expect_ck_b) { | ||
self.off += pos + 2; | ||
return Some(Err(ParserError::InvalidChecksum { | ||
expect: u16::from_le_bytes([expect_ck_a, expect_ck_b]), | ||
got: u16::from_le_bytes([ck_a, ck_b]), | ||
})); | ||
} | ||
let msg_data = &maybe_pack[6..(6 + pack_len)]; | ||
let class_id = maybe_pack[2]; | ||
let msg_id = maybe_pack[3]; | ||
self.off += pos + 6 + pack_len + 2; | ||
return Some(match_packet(class_id, msg_id, msg_data)); | ||
} | ||
None | ||
} | ||
} | ||
|
||
#[test] | ||
fn test_max_payload_len() { | ||
assert!(MAX_PAYLOAD_LEN >= 1240); | ||
} |
Oops, something went wrong.