use std::io;
use crate::engine::{
DecodedPgn, FramePacketType, PacketType, PgnDatabase, RawFrame, Reassembled, Reassembler,
};
pub trait FrameSource {
fn read_frame(&mut self) -> io::Result<Option<RawFrame>>;
}
impl FrameSource for std::sync::mpsc::Receiver<RawFrame> {
fn read_frame(&mut self) -> io::Result<Option<RawFrame>> {
match self.recv() {
Ok(frame) => Ok(Some(frame)),
Err(std::sync::mpsc::RecvError) => Ok(None),
}
}
}
pub struct Decoder<S> {
source: S,
db: &'static PgnDatabase,
reasm: Reassembler,
}
impl<S: FrameSource> Decoder<S> {
pub fn new(source: S, db: &'static PgnDatabase) -> Self {
Self {
source,
db,
reasm: Reassembler::new(),
}
}
pub fn units(&self) -> crate::engine::Units {
self.db.units()
}
}
impl<S: FrameSource> Iterator for Decoder<S> {
type Item = io::Result<DecodedPgn>;
fn next(&mut self) -> Option<Self::Item> {
loop {
let frame = match self.source.read_frame() {
Ok(Some(f)) => f,
Ok(None) => return None,
Err(e) => return Some(Err(e)),
};
let packet_type = self
.db
.first_pgn(frame.pgn)
.or_else(|| self.db.fallback_pgn(frame.pgn))
.map(|p| match p.packet_type {
PacketType::Fast => FramePacketType::Fast,
PacketType::Single => FramePacketType::Single,
_ => FramePacketType::Other,
})
.unwrap_or(FramePacketType::Other);
let assembled = match self.reasm.push(frame, packet_type) {
Reassembled::PassThrough(f) | Reassembled::Complete(f) => f,
Reassembled::Partial => continue,
Reassembled::Error(e) => {
log::warn!("reassembly error: {e}");
continue;
}
};
match self.db.decode(&assembled) {
Ok(d) => return Some(Ok(d)),
Err(e) => {
log::warn!("decode error: {e}");
continue;
}
}
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::engine::Units;
struct VecSource(std::vec::IntoIter<RawFrame>);
impl FrameSource for VecSource {
fn read_frame(&mut self) -> io::Result<Option<RawFrame>> {
Ok(self.0.next())
}
}
fn iso_address_claim() -> RawFrame {
RawFrame::new(
None,
6,
60928,
5,
255,
[0xfb, 0x9b, 0x70, 0x22, 0x00, 0x9b, 0x50, 0xc0],
)
}
#[test]
fn decoder_yields_decoded_from_a_custom_source() {
let db = PgnDatabase::embedded(Units::Metric);
let src = VecSource(vec![iso_address_claim()].into_iter());
let decoded: Vec<_> = Decoder::new(src, db).map(|r| r.expect("io")).collect();
assert_eq!(decoded.len(), 1);
assert_eq!(decoded[0].pgn, 60928);
assert_eq!(decoded[0].id, "isoAddressClaim");
}
#[test]
fn mpsc_receiver_is_a_frame_source() {
let db = PgnDatabase::embedded(Units::Metric);
let (tx, rx) = std::sync::mpsc::channel();
tx.send(iso_address_claim()).unwrap();
drop(tx); let decoded: Vec<_> = Decoder::new(rx, db).map(|r| r.expect("io")).collect();
assert_eq!(decoded.len(), 1);
assert_eq!(decoded[0].pgn, 60928);
}
}