Skip to main content

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}