pub mod ds402;
pub mod emergency;
pub mod lss;
pub mod nmt;
pub mod node;
pub mod object_dict;
pub mod pdo;
pub mod sdo;
pub mod sdo_segment;
pub mod sync;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum CanOpenError {
Nmt(nmt::NmtError),
Od(object_dict::OdError),
Sdo(sdo::SdoError),
Pdo(pdo::PdoError),
InvalidNodeId,
InvalidCobId,
}
impl From<nmt::NmtError> for CanOpenError {
fn from(e: nmt::NmtError) -> Self {
Self::Nmt(e)
}
}
impl From<object_dict::OdError> for CanOpenError {
fn from(e: object_dict::OdError) -> Self {
Self::Od(e)
}
}
impl From<sdo::SdoError> for CanOpenError {
fn from(e: sdo::SdoError) -> Self {
Self::Sdo(e)
}
}
impl From<pdo::PdoError> for CanOpenError {
fn from(e: pdo::PdoError) -> Self {
Self::Pdo(e)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct NodeId(u8);
impl NodeId {
pub const MIN: u8 = 1;
pub const MAX: u8 = 127;
pub const fn new(id: u8) -> Option<Self> {
if id >= Self::MIN && id <= Self::MAX {
Some(Self(id))
} else {
None
}
}
pub const fn new_unchecked(id: u8) -> Self {
Self(id)
}
pub const fn get(self) -> u8 {
self.0
}
pub fn tpdo1_cob_id(self) -> CobId {
CobId(0x180 + self.0 as u32)
}
pub fn rpdo1_cob_id(self) -> CobId {
CobId(0x200 + self.0 as u32)
}
pub fn sdo_rx_cob_id(self) -> CobId {
CobId(0x600 + self.0 as u32)
}
pub fn sdo_tx_cob_id(self) -> CobId {
CobId(0x580 + self.0 as u32)
}
pub fn heartbeat_cob_id(self) -> CobId {
CobId(0x700 + self.0 as u32)
}
pub fn emcy_cob_id(self) -> CobId {
CobId(0x080 + self.0 as u32)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct CobId(pub u32);
impl CobId {
pub const NMT: Self = Self(0x000);
pub const SYNC: Self = Self(0x080);
pub const EMCY_BASE: Self = Self(0x080);
pub const TIMESTAMP: Self = Self(0x100);
pub const SDO_TX_BASE: Self = Self(0x580);
pub const SDO_RX_BASE: Self = Self(0x600);
pub const HEARTBEAT_BASE: Self = Self(0x700);
pub fn new_standard(id: u32) -> Result<Self, CanOpenError> {
if id > 0x7FF {
Err(CanOpenError::InvalidCobId)
} else {
Ok(Self(id))
}
}
pub const fn new_extended(id: u32) -> Self {
Self(id)
}
pub const fn get(self) -> u32 {
self.0
}
}
pub use ds402::{DriveState, Ds402StateMachine};
pub use emergency::{EmergencyEvent, EmergencyProducer};
pub use lss::{LssClientCo, LssClientCoState};
pub use nmt::{
HeartbeatFrame, HeartbeatProducer, NmtCommand, NmtController, NmtError, NmtMessage, NmtState,
NmtStateMachine,
};
pub use node::CanOpenNode;
pub use object_dict::{
AccessType, DataType, ObjectDict, OdEntry, OdEntryValue, OdError, OdIndex, OdValue, StaticOd,
};
pub use pdo::{
CanFrame, Pdo, PdoComm, PdoError, PdoMapEntry, PdoMapping, RpdoConfig, RpdoConsumer,
TpdoConfig, TpdoProducer,
};
pub use sdo::{SdoAbortCode, SdoError, SdoFrame, SdoServer};
pub use sdo_segment::SdoSegmentTransfer;
pub use sync::{SyncConsumer, SyncProducer};
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn node_id_valid_range() {
assert!(NodeId::new(1).is_some());
assert!(NodeId::new(127).is_some());
assert!(NodeId::new(0).is_none());
assert!(NodeId::new(128).is_none());
}
#[test]
fn node_id_cob_ids() {
let node = NodeId::new_unchecked(5);
assert_eq!(node.tpdo1_cob_id().get(), 0x185);
assert_eq!(node.rpdo1_cob_id().get(), 0x205);
assert_eq!(node.sdo_rx_cob_id().get(), 0x605);
assert_eq!(node.sdo_tx_cob_id().get(), 0x585);
assert_eq!(node.heartbeat_cob_id().get(), 0x705);
assert_eq!(node.emcy_cob_id().get(), 0x085);
}
#[test]
fn cob_id_standard_range() {
assert!(CobId::new_standard(0x7FF).is_ok());
assert!(CobId::new_standard(0x800).is_err());
}
#[test]
fn cob_id_constants() {
assert_eq!(CobId::NMT.get(), 0x000);
assert_eq!(CobId::SYNC.get(), 0x080);
assert_eq!(CobId::SDO_TX_BASE.get(), 0x580);
assert_eq!(CobId::SDO_RX_BASE.get(), 0x600);
assert_eq!(CobId::HEARTBEAT_BASE.get(), 0x700);
}
#[test]
fn canopen_error_from_nmt_error() {
let e = CanOpenError::from(nmt::NmtError::UnknownCommand(0xFF));
matches!(e, CanOpenError::Nmt(_));
}
#[test]
fn canopen_error_from_od_error() {
let e = CanOpenError::from(object_dict::OdError::NotFound);
matches!(e, CanOpenError::Od(_));
}
}