zencan_common/
node_id.rs

1//! Types for representing node IDs
2//!
3
4/// An enum representing the node ID of a CANopen node. The node ID must be between 1 and 127 for
5/// configured devices, with the special value of 255 used to represent an unconfigured device.
6#[derive(Debug, Clone, Copy, PartialEq, Eq)]
7#[cfg_attr(feature = "defmt", derive(defmt::Format))]
8pub enum NodeId {
9    /// A special node ID indicating the node is not configured (255)
10    Unconfigured,
11    /// A valid node ID for a configured node
12    Configured(ConfiguredNodeId),
13}
14
15/// A newtype on u8 to enforce valid node ID (1-127)
16#[derive(Debug, Clone, Copy, PartialEq, Eq)]
17#[cfg_attr(feature = "defmt", derive(defmt::Format))]
18pub struct ConfiguredNodeId(u8);
19impl ConfiguredNodeId {
20    /// Try to create a new ConfiguredId
21    ///
22    /// It will fail if value is invalid (i.e. <1 or >127)
23    pub fn new(value: u8) -> Result<Self, InvalidNodeIdError> {
24        if (value > 0 && value < 128) || value == 255 {
25            Ok(ConfiguredNodeId(value))
26        } else {
27            Err(InvalidNodeIdError)
28        }
29    }
30
31    /// Get the raw node ID as a u8
32    pub fn raw(&self) -> u8 {
33        self.0
34    }
35}
36
37impl core::fmt::Display for ConfiguredNodeId {
38    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
39        write!(f, "{}", self.0)
40    }
41}
42
43impl From<ConfiguredNodeId> for u8 {
44    fn from(value: ConfiguredNodeId) -> Self {
45        value.raw()
46    }
47}
48
49impl NodeId {
50    /// Try to create a new NodeId from a u8
51    ///
52    /// Will fail if the value is not a valid node ID
53    pub fn new(value: u8) -> Result<Self, InvalidNodeIdError> {
54        if value == 255 {
55            Ok(NodeId::Unconfigured)
56        } else {
57            ConfiguredNodeId::new(value).map(NodeId::Configured)
58        }
59    }
60
61    /// Get the raw node ID as a u8
62    pub fn raw(&self) -> u8 {
63        match self {
64            NodeId::Unconfigured => 255,
65            NodeId::Configured(node_id_num) => node_id_num.0,
66        }
67    }
68
69    /// Attempt to get the node ID as a ConfiguredNodeId
70    ///
71    /// Returns none if the value is NodeId::Unconfigured
72    pub fn as_configured(&self) -> Option<ConfiguredNodeId> {
73        match self {
74            NodeId::Unconfigured => None,
75            NodeId::Configured(configured_node_id) => Some(*configured_node_id),
76        }
77    }
78
79    /// Return true if the NodeId contains a valid configured ID
80    pub fn is_configured(&self) -> bool {
81        match self {
82            Self::Configured(_) => true,
83            Self::Unconfigured => false,
84        }
85    }
86    /// Return true if the node ID is NodeId::Unconfigured
87    pub fn is_unconfigured(&self) -> bool {
88        match self {
89            Self::Configured(_) => false,
90            Self::Unconfigured => true,
91        }
92    }
93}
94
95/// Error for converting u8 to a NodeId
96#[derive(Debug, Clone, Copy, PartialEq, Eq)]
97pub struct InvalidNodeIdError;
98
99impl core::fmt::Display for InvalidNodeIdError {
100    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
101        write!(f, "Invalid node ID")
102    }
103}
104impl core::error::Error for InvalidNodeIdError {}
105
106impl TryFrom<u8> for NodeId {
107    type Error = InvalidNodeIdError;
108
109    fn try_from(value: u8) -> Result<Self, Self::Error> {
110        if value == 255 {
111            Ok(NodeId::Unconfigured)
112        } else {
113            Ok(NodeId::Configured(ConfiguredNodeId(value)))
114        }
115    }
116}
117
118impl From<NodeId> for u8 {
119    fn from(value: NodeId) -> Self {
120        value.raw()
121    }
122}