use crate::{
error::Error,
ethernet::EthernetFrame,
fmt,
pdu_loop::{
frame_element::{FrameBox, FrameElement, FrameState},
frame_header::EthercatFrameHeader,
},
};
use core::{ptr::NonNull, sync::atomic::AtomicU8};
use ethercrab_wire::EtherCrabWireSized;
#[derive(Debug)]
pub struct SendableFrame<'sto> {
pub(in crate::pdu_loop) inner: FrameBox<'sto>,
}
unsafe impl Send for SendableFrame<'_> {}
impl<'sto> SendableFrame<'sto> {
pub(crate) fn claim_sending(
frame: NonNull<FrameElement<0>>,
pdu_idx: &'sto AtomicU8,
frame_data_len: usize,
) -> Option<Self> {
let frame = unsafe { FrameElement::claim_sending(frame)? };
Some(Self {
inner: FrameBox::new(frame, pdu_idx, frame_data_len),
})
}
fn mark_sent(&self) {
fmt::trace!("Frame index {} is sent", self.inner.storage_slot_index());
self.inner.set_state(FrameState::Sent);
}
pub(crate) fn storage_slot_index(&self) -> u8 {
self.inner.storage_slot_index()
}
fn release_sending_claim(&self) {
self.inner.set_state(FrameState::Sendable);
}
fn as_bytes(&self) -> &[u8] {
let frame = self.inner.ethernet_frame().into_inner();
let len = EthernetFrame::<&[u8]>::buffer_len(
EthercatFrameHeader::PACKED_LEN + self.inner.pdu_payload_len(),
);
&frame[0..len]
}
#[allow(clippy::len_without_is_empty)]
pub fn len(&self) -> usize {
self.as_bytes().len()
}
pub fn send_blocking(
self,
send: impl FnOnce(&[u8]) -> Result<usize, Error>,
) -> Result<usize, Error> {
let len = self.as_bytes().len();
match send(self.as_bytes()) {
Ok(bytes_sent) if bytes_sent == len => {
self.mark_sent();
Ok(bytes_sent)
}
Ok(bytes_sent) => {
self.release_sending_claim();
Err(Error::PartialSend {
len,
sent: bytes_sent,
})
}
Err(res) => {
self.release_sending_claim();
Err(res)
}
}
}
}