use std::{
collections::{HashMap, VecDeque},
fmt::Display,
};
use tracing::{debug, trace};
use crate::framing::{integrity::Integrity, DecodedFrame, Scid, Vcid};
use crate::spacepacket::{Packet, PrimaryHeader};
struct VcidTracker {
vcid: Vcid,
cache: Vec<u8>,
rs_corrected: bool,
sync: bool,
}
impl VcidTracker {
fn new(vcid: Vcid) -> Self {
VcidTracker {
vcid,
sync: false,
cache: vec![],
rs_corrected: false,
}
}
fn reset(&mut self) {
self.cache.clear();
self.sync = false;
self.rs_corrected = false;
}
}
impl Display for VcidTracker {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(
f,
"VcidTracker{{vcid={}, sync={}, cache_len={}, rs_corrected:{}}}",
self.vcid,
self.sync,
self.cache.len(),
self.rs_corrected
)
}
}
#[derive(Debug, Clone)]
pub struct DecodedPacket {
pub scid: Scid,
pub vcid: Vcid,
pub packet: Packet,
}
struct FramedPacketIter<I>
where
I: Iterator<Item = DecodedFrame> + Send,
{
frames: I,
izone_length: usize,
trailer_length: usize,
cache: HashMap<Vcid, VcidTracker>,
ready: VecDeque<DecodedPacket>,
}
impl<I> Iterator for FramedPacketIter<I>
where
I: Iterator<Item = DecodedFrame> + Send,
{
type Item = DecodedPacket;
fn next(&mut self) -> Option<Self::Item> {
if let Some(packet) = self.ready.pop_front() {
return Some(packet);
}
'next_frame: loop {
let frame = self.frames.next();
if frame.is_none() {
trace!("no more frames");
break;
}
let DecodedFrame {
frame,
missing,
integrity,
} = frame.unwrap();
let mpdu = frame.mpdu(self.izone_length, self.trailer_length).unwrap();
let tracker = self
.cache
.entry(frame.header.vcid)
.or_insert(VcidTracker::new(frame.header.vcid));
match integrity {
Some(Integrity::Corrected) => {
debug!(vcid = %frame.header.vcid, "corrected frame");
tracker.rs_corrected = true;
}
Some(Integrity::Uncorrectable) | Some(Integrity::HasErrors) => {
debug!(vcid = %frame.header.vcid, tracker = %tracker, "uncorrectable or errored frame, dropping tracker");
tracker.reset();
continue;
}
_ => {}
}
if missing > 0 {
trace!(vcid = frame.header.vcid, tracker=%tracker, missing=missing, "missing frames, dropping tracker");
tracker.reset();
}
if tracker.sync {
tracker.cache.extend_from_slice(mpdu.payload());
} else {
if !mpdu.has_header() {
trace!(vcid = %frame.header.vcid, tracker = %tracker, "frames w/o mpdu, dropping");
continue;
}
if mpdu.is_fill() {
trace!(vcid = %frame.header.vcid, tracker = %tracker, "fill mpdu, dropping");
continue;
}
if mpdu.header_offset() > mpdu.payload().len() {
debug!(
"invalid MPDU header offset; value={} buf size={}",
mpdu.header_offset(),
mpdu.payload().len()
);
continue;
}
tracker.sync = true;
tracker.cache = mpdu.payload()[mpdu.header_offset()..].to_vec();
}
if tracker.cache.len() < PrimaryHeader::LEN {
continue 'next_frame;
}
let mut header =
PrimaryHeader::decode(&tracker.cache).expect("failed to decode primary header");
if !valid_packet_header(&header) {
tracker.reset();
continue;
}
let mut need = header.len_minus1 as usize + 1 + PrimaryHeader::LEN;
if tracker.cache.len() < need {
continue 'next_frame;
}
loop {
let (data, tail) = tracker.cache.split_at(need);
let packet = DecodedPacket {
scid: frame.header.scid,
vcid: frame.header.vcid,
packet: Packet {
header: PrimaryHeader::decode(data)
.expect("failed to decode primary header"),
data: data.to_vec(),
offset: 0,
},
};
tracker.cache = tail.to_vec();
self.ready.push_back(packet);
if tracker.cache.len() < PrimaryHeader::LEN {
break;
}
header =
PrimaryHeader::decode(&tracker.cache).expect("failed to decode primary header");
if !valid_packet_header(&header) {
tracker.reset();
break;
}
need = header.len_minus1 as usize + 1 + PrimaryHeader::LEN;
if tracker.cache.len() < need {
break;
}
}
return self.ready.pop_front();
}
self.ready.pop_front()
}
}
fn valid_packet_header(header: &PrimaryHeader) -> bool {
if header.version != 0 || header.type_flag != 0 {
debug!("bad packet version or type, dropping {header:?}");
return false;
}
true
}
pub fn decode_framed_packets<I>(
frames: I,
izone_length: usize,
trailer_length: usize,
) -> impl Iterator<Item = DecodedPacket> + Send
where
I: Iterator<Item = DecodedFrame> + Send,
{
FramedPacketIter {
frames: frames.filter(|dc| !dc.frame.is_fill()),
izone_length,
trailer_length,
cache: HashMap::new(),
ready: VecDeque::new(),
}
}