ethercrab/pdu_loop/pdu_tx.rs
1use super::{frame_element::sendable_frame::SendableFrame, storage::PduStorageRef};
2use core::{sync::atomic::Ordering, task::Waker};
3
4/// EtherCAT frame transmit adapter.
5pub struct PduTx<'sto> {
6 storage: PduStorageRef<'sto>,
7}
8
9impl<'sto> PduTx<'sto> {
10 pub(in crate::pdu_loop) fn new(storage: PduStorageRef<'sto>) -> Self {
11 Self { storage }
12 }
13
14 /// The number of frames that can be in flight at once.
15 pub fn capacity(&self) -> usize {
16 self.storage.num_frames
17 }
18
19 /// Get the next sendable frame, if any are available.
20 // NOTE: Mutable so it can only be used in one task.
21 pub fn next_sendable_frame(&mut self) -> Option<SendableFrame<'sto>> {
22 for idx in 0..self.storage.num_frames {
23 if self.should_exit() {
24 return None;
25 }
26
27 let frame = self.storage.frame_at_index(idx);
28
29 let Some(sending) = SendableFrame::claim_sending(
30 frame,
31 self.storage.pdu_idx,
32 self.storage.frame_data_len,
33 ) else {
34 continue;
35 };
36
37 return Some(sending);
38 }
39
40 None
41 }
42
43 /// Set or replace the PDU loop waker.
44 ///
45 /// The waker must be set otherwise the future in charge of sending new packets will not be
46 /// woken again, causing a timeout error.
47 ///
48 /// # Examples
49 ///
50 /// ```rust,no_run
51 /// # use ethercrab::PduStorage;
52 /// use core::future::poll_fn;
53 /// use core::task::Poll;
54 ///
55 /// # static PDU_STORAGE: PduStorage<2, { PduStorage::element_size(2) }> = PduStorage::new();
56 /// let (pdu_tx, _pdu_rx, _pdu_loop) = PDU_STORAGE.try_split().expect("can only split once");
57 ///
58 /// poll_fn(|ctx| {
59 /// // Set the waker so this future is polled again when new EtherCAT frames are ready to
60 /// // be sent.
61 /// pdu_tx.replace_waker(ctx.waker());
62 ///
63 /// // Send and receive packets over the network interface here
64 ///
65 /// Poll::<()>::Pending
66 /// });
67 /// ```
68 #[cfg_attr(
69 any(target_os = "windows", target_os = "macos", not(feature = "std")),
70 allow(unused)
71 )]
72 pub fn replace_waker(&self, waker: &Waker) {
73 self.storage.tx_waker.register(waker);
74 }
75
76 /// Returns `true` if the PDU sender should exit.
77 ///
78 /// This will be triggered by [`MainDevice::release_all`](crate::MainDevice::release_all). When
79 /// giving back ownership of the `PduTx`, be sure to call [`release`](crate::PduTx::release) to
80 /// ensure all internal state is correct before reuse.
81 pub fn should_exit(&self) -> bool {
82 self.storage.exit_flag.load(Ordering::Acquire)
83 }
84
85 /// Reset this object ready for reuse.
86 pub fn release(self) -> Self {
87 self.storage.exit_flag.store(false, Ordering::Relaxed);
88
89 self
90 }
91}