1use core::fmt;
4
5pub type Result<T> = core::result::Result<T, Error>;
7
8#[derive(Debug, Clone, PartialEq, Eq)]
10#[non_exhaustive]
11pub enum Error {
12 BadLength,
14 Overflow,
16 ObjectNotFound,
18 InvalidNodeId,
20 ReadOnly,
22 WriteOnly,
24 TypeMismatch,
26 DictionaryFull,
28 UnsupportedTransfer,
31 UnexpectedCommand,
33 UnknownState,
35 PdoTooLong,
37 MappingFull,
39 InvalidSyncCounter,
41 ToggleMismatch,
43}
44
45impl fmt::Display for Error {
46 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
47 let msg = match self {
48 Error::BadLength => "frame or buffer length invalid for operation",
49 Error::Overflow => "value does not fit the target CANopen data type",
50 Error::ObjectNotFound => "no object at the requested index/subindex",
51 Error::InvalidNodeId => "node id out of range (must be 1..=127)",
52 Error::ReadOnly => "object is not writable",
53 Error::WriteOnly => "object is not readable",
54 Error::TypeMismatch => "value data type does not match the object",
55 Error::DictionaryFull => "object dictionary capacity exhausted",
56 Error::UnsupportedTransfer => "value cannot be carried by this transfer type",
57 Error::UnexpectedCommand => "unexpected SDO command byte in response",
58 Error::UnknownState => "unrecognised NMT state in heartbeat frame",
59 Error::PdoTooLong => "mapped PDO objects exceed eight bytes",
60 Error::MappingFull => "PDO mapping capacity exhausted",
61 Error::InvalidSyncCounter => "SYNC counter overflow must be 0 or 2..=240",
62 Error::ToggleMismatch => "SDO segment toggle bit did not alternate",
63 };
64 f.write_str(msg)
65 }
66}
67
68#[cfg(feature = "std")]
69extern crate std;
70#[cfg(feature = "std")]
71impl std::error::Error for Error {}
72
73#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
79pub struct NodeId(u8);
80
81impl NodeId {
82 pub const BROADCAST: NodeId = NodeId(0);
84
85 pub fn new(id: u8) -> Result<Self> {
89 if (1..=127).contains(&id) {
90 Ok(NodeId(id))
91 } else {
92 Err(Error::InvalidNodeId)
93 }
94 }
95
96 pub const fn raw(self) -> u8 {
98 self.0
99 }
100}
101
102#[cfg(test)]
103mod tests {
104 use super::*;
105
106 #[test]
107 fn node_id_accepts_valid_range() {
108 assert_eq!(NodeId::new(1).unwrap().raw(), 1);
109 assert_eq!(NodeId::new(127).unwrap().raw(), 127);
110 }
111
112 #[test]
113 fn node_id_rejects_out_of_range() {
114 assert_eq!(NodeId::new(0), Err(Error::InvalidNodeId));
115 assert_eq!(NodeId::new(128), Err(Error::InvalidNodeId));
116 }
117
118 #[test]
119 fn broadcast_is_zero() {
120 assert_eq!(NodeId::BROADCAST.raw(), 0);
121 }
122}