use crate::{
error::{Error, PduError},
fmt,
pdu_loop::{
frame_element::{FrameBox, FrameState, created_frame::PduResponseHandle},
pdu_header::PduHeader,
},
};
use core::{marker::PhantomData, ops::Deref, ptr::NonNull};
use ethercrab_wire::{EtherCrabWireRead, EtherCrabWireSized};
#[derive(Debug)]
pub struct ReceivedFrame<'sto> {
pub(in crate::pdu_loop::frame_element) inner: FrameBox<'sto>,
}
impl<'sto> ReceivedFrame<'sto> {
pub(in crate::pdu_loop) fn new(inner: FrameBox<'sto>) -> ReceivedFrame<'sto> {
Self { inner }
}
#[cfg(test)]
pub(crate) fn from_frame_element_for_test_only(
frame: NonNull<super::FrameElement<0>>,
pdu_idx: &'sto core::sync::atomic::AtomicU8,
max_len: usize,
) -> ReceivedFrame<'sto> {
let f = Self {
inner: FrameBox::new(frame, pdu_idx, max_len),
};
f.inner.set_state(FrameState::RxProcessing);
f
}
pub fn first_pdu(self, handle: PduResponseHandle) -> Result<ReceivedPdu<'sto>, Error> {
let buf = self.inner.pdu_buf();
let pdu_header = PduHeader::unpack_from_slice(buf)?;
let payload_len = usize::from(pdu_header.flags.len());
if buf.len() < payload_len + 2 {
return Err(Error::Pdu(PduError::TooLong));
}
if pdu_header.command_code != handle.command_code {
return Err(Error::Pdu(PduError::Decode));
}
if pdu_header.index != handle.pdu_idx {
return Err(Error::Pdu(PduError::InvalidIndex(pdu_header.index)));
}
let payload_ptr = unsafe {
NonNull::new_unchecked(
buf.get(PduHeader::PACKED_LEN..)
.ok_or(Error::Internal)?
.as_ptr()
.cast_mut(),
)
};
let working_counter = u16::unpack_from_slice(
buf.get((PduHeader::PACKED_LEN + payload_len)..)
.ok_or(Error::Internal)?,
)?;
Ok(ReceivedPdu {
data_start: payload_ptr,
len: payload_len,
working_counter,
_storage: PhantomData,
})
}
#[allow(unused)]
pub fn pdu<'pdu>(&'sto self, handle: PduResponseHandle) -> Result<ReceivedPdu<'pdu>, Error>
where
'sto: 'pdu,
{
let mut buf = self.inner.pdu_buf();
for _ in 0..handle.index_in_frame {
let pdu_header = PduHeader::unpack_from_slice(buf)?;
let payload_len = usize::from(pdu_header.flags.len());
let this_pdu_len = PduHeader::PACKED_LEN + payload_len + 2;
buf = buf.get(this_pdu_len..).ok_or(Error::Internal)?;
}
let pdu_header = PduHeader::unpack_from_slice(buf)?;
if pdu_header.command_code != handle.command_code {
return Err(Error::Pdu(PduError::Decode));
}
if pdu_header.index != handle.pdu_idx {
return Err(Error::Pdu(PduError::InvalidIndex(pdu_header.index)));
}
let payload_len = usize::from(pdu_header.flags.len());
if buf.len() < payload_len + 2 {
return Err(Error::Pdu(PduError::TooLong));
}
let payload_ptr = unsafe {
NonNull::new_unchecked(
buf.get(PduHeader::PACKED_LEN..)
.ok_or(Error::Internal)?
.as_ptr()
.cast_mut(),
)
};
let working_counter = u16::unpack_from_slice(
buf.get((PduHeader::PACKED_LEN + payload_len)..)
.ok_or(Error::Internal)?,
)?;
Ok(ReceivedPdu {
data_start: payload_ptr,
len: payload_len,
working_counter,
_storage: PhantomData,
})
}
pub fn into_pdu_iter(self) -> ReceivedPduIter<'sto> {
ReceivedPduIter {
frame: self,
buf_pos: 0,
}
}
}
impl Drop for ReceivedFrame<'_> {
fn drop(&mut self) {
fmt::unwrap!(
self.inner
.swap_state(FrameState::RxProcessing, FrameState::None)
);
self.inner.clear_first_pdu();
}
}
pub struct ReceivedPduIter<'sto> {
frame: ReceivedFrame<'sto>,
buf_pos: usize,
}
impl<'sto> Iterator for ReceivedPduIter<'sto> {
type Item = Result<ReceivedPdu<'sto>, Error>;
fn next(&mut self) -> Option<Self::Item> {
if self.frame.inner.pdu_payload_len() == 0 {
return None;
}
let buf = self.frame.inner.pdu_buf().get(self.buf_pos..)?;
let pdu_header = match PduHeader::unpack_from_slice(buf) {
Ok(h) => h,
Err(e) => return Some(Err(e.into())),
};
let payload_len = usize::from(pdu_header.flags.len());
let this_pdu_len = PduHeader::PACKED_LEN + payload_len + 2;
if buf.len() < payload_len + 2 {
return Some(Err(Error::Pdu(PduError::TooLong)));
}
let payload_ptr = unsafe {
NonNull::new_unchecked(buf.get(PduHeader::PACKED_LEN..)?.as_ptr().cast_mut())
};
let working_counter = match buf
.get((PduHeader::PACKED_LEN + payload_len)..)
.ok_or(Error::Internal)
.and_then(|b| u16::unpack_from_slice(b).map_err(Error::from))
{
Ok(wkc) => wkc,
Err(e) => return Some(Err(e)),
};
let res = Ok(ReceivedPdu {
data_start: payload_ptr,
len: payload_len,
working_counter,
_storage: PhantomData,
});
if pdu_header.flags.more_follows {
self.buf_pos += this_pdu_len;
}
else {
self.buf_pos = usize::MAX
}
Some(res)
}
}
#[derive(Debug)]
pub struct ReceivedPdu<'sto> {
data_start: NonNull<u8>,
len: usize,
pub(crate) working_counter: u16,
_storage: PhantomData<&'sto ()>,
}
impl ReceivedPdu<'_> {
pub fn len(&self) -> usize {
self.len
}
pub fn trim_front(&mut self, ct: usize) {
let ct = ct.min(self.len());
self.data_start = unsafe { NonNull::new_unchecked(self.data_start.as_ptr().add(ct)) };
}
pub fn wkc(self, expected: u16) -> Result<Self, Error> {
if self.working_counter == expected {
Ok(self)
} else {
Err(Error::WorkingCounter {
expected,
received: self.working_counter,
})
}
}
pub fn maybe_wkc(self, expected: Option<u16>) -> Result<Self, Error> {
match expected {
Some(expected) => self.wkc(expected),
None => Ok(self),
}
}
}
unsafe impl Send for ReceivedPdu<'_> {}
impl Deref for ReceivedPdu<'_> {
type Target = [u8];
fn deref(&self) -> &Self::Target {
let len = self.len();
unsafe { core::slice::from_raw_parts(self.data_start.as_ptr(), len) }
}
}