zencan_node/
node_mbox.rs

1//! Implements mailbox for receiving CAN messages
2use defmt_or_log::warn;
3use zencan_common::{
4    messages::{CanId, CanMessage},
5    AtomicCell,
6};
7
8use crate::{lss_slave::LssReceiver, pdo::Pdo, sdo_server::SdoReceiver};
9
10/// A data structure to be shared between a receiving thread (e.g. a CAN controller IRQ) and the
11/// [`Node`](crate::Node) object.
12///
13/// Incoming messages should be passed to [NodeMbox::store_message].
14#[allow(missing_debug_implementations)]
15pub struct NodeMbox {
16    rx_pdos: &'static [Pdo],
17    sdo_cob_id: AtomicCell<Option<CanId>>,
18    sdo_receiver: SdoReceiver,
19    nmt_mbox: AtomicCell<Option<CanMessage>>,
20    lss_receiver: LssReceiver,
21    sync_flag: AtomicCell<bool>,
22    notify_cb: AtomicCell<Option<&'static (dyn Fn() + Sync)>>,
23}
24
25impl NodeMbox {
26    /// Create a new NodeMbox
27    ///
28    /// # Args
29    ///
30    /// - `rx_pdos`: A slice of Pdo objects for all of the receive PDOs
31    pub const fn new(rx_pdos: &'static [Pdo], sdo_buffer: &'static mut [u8]) -> Self {
32        let sdo_cob_id = AtomicCell::new(None);
33        let sdo_receiver = SdoReceiver::new(sdo_buffer);
34        let nmt_mbox = AtomicCell::new(None);
35        let lss_receiver = LssReceiver::new();
36        let sync_flag = AtomicCell::new(false);
37        let notify_cb = AtomicCell::new(None);
38        Self {
39            rx_pdos,
40            sdo_cob_id,
41            sdo_receiver,
42            nmt_mbox,
43            lss_receiver,
44            sync_flag,
45            notify_cb,
46        }
47    }
48
49    /// Set a callback for notification when a message is received and requires processing.
50    ///
51    /// It must be static. Usually this will be a static fn, but in some circumstances, it may be
52    /// desirable to use Box::leak to pass a heap allocated closure instead.
53    pub fn set_process_notify_callback(&self, callback: &'static (dyn Fn() + Sync)) {
54        self.notify_cb.store(Some(callback));
55    }
56
57    fn notify(&self) {
58        if let Some(notify_cb) = self.notify_cb.load() {
59            notify_cb();
60        }
61    }
62
63    pub(crate) fn set_sdo_cob_id(&self, cob_id: Option<CanId>) {
64        self.sdo_cob_id.store(cob_id);
65    }
66
67    pub(crate) fn sdo_receiver(&self) -> &SdoReceiver {
68        &self.sdo_receiver
69    }
70
71    pub(crate) fn read_nmt_mbox(&self) -> Option<CanMessage> {
72        self.nmt_mbox.take()
73    }
74
75    pub(crate) fn lss_receiver(&self) -> &LssReceiver {
76        &self.lss_receiver
77    }
78
79    pub(crate) fn read_sync_flag(&self) -> bool {
80        self.sync_flag.take()
81    }
82
83    /// Store a received CAN message
84    pub fn store_message(&self, msg: CanMessage) -> Result<(), CanMessage> {
85        let id = msg.id();
86        if id == zencan_common::messages::NMT_CMD_ID {
87            self.nmt_mbox.store(Some(msg));
88            self.notify();
89            return Ok(());
90        }
91
92        if id == zencan_common::messages::SYNC_ID {
93            self.sync_flag.store(true);
94            self.notify();
95            return Ok(());
96        }
97
98        if id == zencan_common::messages::LSS_REQ_ID {
99            if let Ok(lss_req) = msg.data().try_into() {
100                if self.lss_receiver.handle_req(lss_req) {
101                    self.notify();
102                }
103            } else {
104                warn!("Invalid LSS request");
105                return Err(msg);
106            }
107            return Ok(());
108        }
109
110        for rpdo in self.rx_pdos {
111            if !rpdo.valid() {
112                continue;
113            }
114            if id == rpdo.cob_id() {
115                let mut data = [0u8; 8];
116                data[0..msg.data().len()].copy_from_slice(msg.data());
117                rpdo.buffered_value.store(Some(data));
118                return Ok(());
119            }
120        }
121
122        if let Some(cob_id) = self.sdo_cob_id.load() {
123            if id == cob_id {
124                self.sdo_receiver.handle_req(msg.data());
125            }
126        }
127
128        Err(msg)
129    }
130}