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