Skip to content

Commit

Permalink
Use own proc macro to generate code to deal with packets
Browse files Browse the repository at this point in the history
Features:
- removes unnecessary dependicies
- more type safe code
- faster parser: 3x
fixes #1
  • Loading branch information
Dushistov committed Mar 12, 2020
1 parent 567263b commit 5390375
Show file tree
Hide file tree
Showing 20 changed files with 3,463 additions and 1,499 deletions.
3 changes: 3 additions & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,9 @@ matrix:
allow_failures:
- rust: nightly
fast_finish: true
before_script:
- rustup component add rustfmt-preview
- which rustfmt
script:
- cargo test --verbose --workspace
- cargo test --release --verbose --workspace
Expand Down
12 changes: 6 additions & 6 deletions ublox/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,12 @@ default = []
serial = ["serialport", "crc"]

[dependencies]
serde = "1.0"
serde_derive = "1.0"
bincode = "1.2.1"
chrono = "0.4"
ublox_derive = "0.0.0"
ublox_derive = "0.0.1"
serialport = { version = "3.3.0", optional = true }
crc = { version = "1.8.1", optional = true }
num-derive = "0.3.0"
num-traits = "0.2.11"
bitflags = "1.2.1"

[dev-dependencies]
rand = "0.7.3"
cpu-time = "1.0.0"
29 changes: 14 additions & 15 deletions ublox/examples/serial.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,24 +6,23 @@ mod serial {

pub fn main() {
let mut dev = Device::new("/dev/ttyUSB0").unwrap();

let pos = Position {
lon: -97.5,
lat: 30.2,
alt: 200.0,
};
println!("Setting AID data...");
match dev.load_aid_data(Some(pos), Some(Utc::now())) {
Err(e) => {
println!("Got error setting AID data: {:?}", e);
}
_ => {}
}

/*
let pos = Position {
lon: -97.5,
lat: 30.2,
alt: 200.0,
};
println!("Setting AID data...");
match dev.load_aid_data(Some(pos), Some(Utc::now())) {
Err(e) => {
println!("Got error setting AID data: {:?}", e);
}
_ => {}
}
loop {
dev.poll_for(Duration::from_millis(500)).unwrap();
println!("{:?}", dev.get_solution());
}
} */
}
}

Expand Down
73 changes: 58 additions & 15 deletions ublox/src/error.rs
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 {}
9 changes: 6 additions & 3 deletions ublox/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,16 @@
//! At time of writing this library is developed for a device which behaves like
//! a NEO-6M device.
pub use crate::segmenter::Segmenter;
#[cfg(feature = "serial")]
pub use crate::serialport::{Device, ResetType};
pub use crate::ubx_packets::*;
pub use crate::{
error::{MemWriterError, ParserError},
parser::{Parser, ParserIter},
ubx_packets::*,
};

mod error;
mod segmenter;
mod parser;
#[cfg(feature = "serial")]
mod serialport;
mod ubx_packets;
121 changes: 121 additions & 0 deletions ublox/src/parser.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
use crate::{
error::ParserError,
ubx_packets::{match_packet, ubx_checksum, PacketRef, SYNC_CHAR_1, SYNC_CHAR_2},
};

/// Some big number, TODO: need validation against all known packets
const MAX_PACK_LEN: usize = 1022;

/// 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<'a, 'b, 'c>(&'a mut self, new_data: &'b [u8]) -> ParserIter<'c>
where
'a: 'c,
{
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 = match data.iter().position(|x| *x == SYNC_CHAR_1) {
Some(x) => x,
None => return None,
};

if (pos + 1) >= data.len() {
return None;
}
if data[pos + 1] != SYNC_CHAR_2 {
self.off += pos + 1;
continue;
}

if (pos + 5) >= data.len() {
return None;
}

let pack_len: usize = u16::from_le_bytes([data[pos + 4], data[pos + 5]]).into();
if pack_len > MAX_PACK_LEN {
self.off += pos + 1;
continue;
}
if (pos + pack_len + 6 + 2 - 1) >= data.len() {
return None;
}
let (ck_a, ck_b) = ubx_checksum(&data[(pos + 2)..(pos + pack_len + 4 + 2)]);

let (expect_ck_a, expect_ck_b) =
(data[pos + 6 + pack_len], data[pos + 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 = &data[(pos + 6)..(pos + 6 + pack_len)];
let class_id = data[pos + 2];
let msg_id = data[pos + 3];
self.off += pos + 6 + pack_len + 2;
return Some(match_packet(class_id, msg_id, msg_data));
}
None
}
}
Loading

0 comments on commit 5390375

Please sign in to comment.