use defmt_or_log::warn;
use zencan_common::{
messages::{CanId, CanMessage, SyncObject},
AtomicCell,
};
use crate::{
lss_slave::LssReceiver, pdo::Pdo, priority_queue::PriorityQueue, sdo_server::SdoComms,
};
pub trait CanMessageQueue: Send + Sync {
fn push(&self, msg: CanMessage) -> Result<(), CanMessage>;
fn pop(&self) -> Option<CanMessage>;
}
impl<const N: usize> CanMessageQueue for PriorityQueue<N, CanMessage> {
fn push(&self, msg: CanMessage) -> Result<(), CanMessage> {
let prio = msg.id().raw();
self.push(prio, msg)
}
fn pop(&self) -> Option<CanMessage> {
self.pop()
}
}
#[allow(missing_debug_implementations)]
pub struct NodeMbox {
rx_pdos: &'static [Pdo<'static>],
tx_pdos: &'static [Pdo<'static>],
sdo_tx_cob_id: AtomicCell<Option<CanId>>,
sdo_rx_cob_id: AtomicCell<Option<CanId>>,
sdo_comms: SdoComms,
nmt_mbox: AtomicCell<Option<CanMessage>>,
lss_receiver: LssReceiver,
sync_flag: AtomicCell<Option<SyncObject>>,
process_notify_cb: AtomicCell<Option<&'static (dyn Fn() + Sync)>>,
transmit_notify_cb: AtomicCell<Option<&'static (dyn Fn() + Sync)>>,
tx_queue: &'static dyn CanMessageQueue,
}
impl NodeMbox {
pub const fn new(
rx_pdos: &'static [Pdo],
tx_pdos: &'static [Pdo],
tx_queue: &'static dyn CanMessageQueue,
sdo_buffer: &'static mut [u8],
) -> Self {
let sdo_rx_cob_id = AtomicCell::new(None);
let sdo_tx_cob_id = AtomicCell::new(None);
let sdo_comms = SdoComms::new(sdo_buffer);
let nmt_mbox = AtomicCell::new(None);
let lss_receiver = LssReceiver::new();
let sync_flag = AtomicCell::new(None);
let process_notify_cb = AtomicCell::new(None);
let transmit_notify_cb = AtomicCell::new(None);
Self {
rx_pdos,
tx_pdos,
sdo_rx_cob_id,
sdo_tx_cob_id,
sdo_comms,
nmt_mbox,
lss_receiver,
sync_flag,
process_notify_cb,
transmit_notify_cb,
tx_queue,
}
}
pub fn set_process_notify_callback(&self, callback: &'static (dyn Fn() + Sync)) {
self.process_notify_cb.store(Some(callback));
}
fn process_notify(&self) {
if let Some(notify_cb) = self.process_notify_cb.load() {
notify_cb();
}
}
pub fn set_transmit_notify_callback(&self, callback: &'static (dyn Fn() + Sync)) {
self.transmit_notify_cb.store(Some(callback));
}
pub(crate) fn transmit_notify(&self) {
if let Some(notify_cb) = self.transmit_notify_cb.load() {
notify_cb();
}
}
pub(crate) fn set_sdo_rx_cob_id(&self, cob_id: Option<CanId>) {
self.sdo_rx_cob_id.store(cob_id);
}
pub(crate) fn set_sdo_tx_cob_id(&self, cob_id: Option<CanId>) {
self.sdo_tx_cob_id.store(cob_id);
}
pub(crate) fn sdo_comms(&self) -> &SdoComms {
&self.sdo_comms
}
pub(crate) fn read_nmt_mbox(&self) -> Option<CanMessage> {
self.nmt_mbox.take()
}
pub(crate) fn lss_receiver(&self) -> &LssReceiver {
&self.lss_receiver
}
pub(crate) fn read_sync_flag(&self) -> Option<SyncObject> {
self.sync_flag.take()
}
pub fn store_message(&self, msg: CanMessage) -> Result<(), CanMessage> {
let id = msg.id();
if id == zencan_common::messages::NMT_CMD_ID {
self.nmt_mbox.store(Some(msg));
self.process_notify();
return Ok(());
}
if id == zencan_common::messages::SYNC_ID {
let sync_object = SyncObject::from(msg);
self.sync_flag.store(Some(sync_object));
self.process_notify();
return Ok(());
}
if id == zencan_common::messages::LSS_REQ_ID {
if let Ok(lss_req) = msg.data().try_into() {
if self.lss_receiver.handle_req(lss_req) {
self.process_notify();
}
} else {
warn!("Invalid LSS request");
return Err(msg);
}
return Ok(());
}
for rpdo in self.rx_pdos {
if !rpdo.valid() {
continue;
}
if id == rpdo.cob_id() {
let mut data = [0u8; 8];
data[0..msg.data().len()].copy_from_slice(msg.data());
rpdo.buffered_value.store(Some(data));
return Ok(());
}
}
if let Some(cob_id) = self.sdo_rx_cob_id.load() {
if id == cob_id {
self.sdo_comms.handle_req(msg.data());
}
}
Err(msg)
}
pub fn next_transmit_message(&self) -> Option<CanMessage> {
for pdo in self.tx_pdos.iter() {
if let Some(buf) = pdo.buffered_value.take() {
return Some(CanMessage::new(pdo.cob_id(), &buf));
}
}
if let Some(msg) = self.tx_queue.pop() {
return Some(msg);
}
if let Some(msg) = self.sdo_comms.next_transmit_message() {
if let Some(id) = self.sdo_tx_cob_id.load() {
return Some(CanMessage::new(id, &msg));
}
}
None
}
pub fn queue_transmit_message(&self, msg: CanMessage) -> Result<(), CanMessage> {
self.tx_queue.push(msg)
}
}