canopen_rs/
object_dictionary.rs1use heapless::LinearMap;
14
15use crate::datatypes::Value;
16use crate::{Error, Result};
17
18#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
24pub struct Address {
25 pub index: u16,
27 pub subindex: u8,
29}
30
31impl Address {
32 pub const fn new(index: u16, subindex: u8) -> Self {
34 Self { index, subindex }
35 }
36}
37
38#[derive(Debug, Clone, Copy, PartialEq, Eq)]
40pub enum AccessType {
41 Ro,
43 Wo,
45 Rw,
47 Const,
49}
50
51impl AccessType {
52 pub const fn is_readable(self) -> bool {
54 matches!(self, AccessType::Ro | AccessType::Rw | AccessType::Const)
55 }
56
57 pub const fn is_writable(self) -> bool {
59 matches!(self, AccessType::Wo | AccessType::Rw)
60 }
61}
62
63#[derive(Debug, Clone, Copy, PartialEq)]
65pub struct Entry {
66 pub value: Value,
68 pub access: AccessType,
70}
71
72impl Entry {
73 pub const fn rw(value: Value) -> Self {
75 Self {
76 value,
77 access: AccessType::Rw,
78 }
79 }
80
81 pub const fn ro(value: Value) -> Self {
83 Self {
84 value,
85 access: AccessType::Ro,
86 }
87 }
88
89 pub const fn constant(value: Value) -> Self {
91 Self {
92 value,
93 access: AccessType::Const,
94 }
95 }
96}
97
98#[derive(Debug)]
101pub struct ObjectDictionary<const N: usize> {
102 entries: LinearMap<Address, Entry, N>,
103}
104
105impl<const N: usize> Default for ObjectDictionary<N> {
106 fn default() -> Self {
107 Self::new()
108 }
109}
110
111impl<const N: usize> ObjectDictionary<N> {
112 pub const fn new() -> Self {
114 Self {
115 entries: LinearMap::new(),
116 }
117 }
118
119 pub fn insert(&mut self, addr: Address, entry: Entry) -> Result<()> {
123 self.entries
124 .insert(addr, entry)
125 .map(|_| ())
126 .map_err(|_| Error::DictionaryFull)
127 }
128
129 pub fn entry(&self, addr: Address) -> Option<&Entry> {
131 self.entries.get(&addr)
132 }
133
134 pub fn read(&self, addr: Address) -> Result<Value> {
139 let entry = self.entries.get(&addr).ok_or(Error::ObjectNotFound)?;
140 if !entry.access.is_readable() {
141 return Err(Error::WriteOnly);
142 }
143 Ok(entry.value)
144 }
145
146 pub fn write(&mut self, addr: Address, value: Value) -> Result<()> {
153 let entry = self.entries.get_mut(&addr).ok_or(Error::ObjectNotFound)?;
154 if !entry.access.is_writable() {
155 return Err(Error::ReadOnly);
156 }
157 if entry.value.data_type() != value.data_type() {
158 return Err(Error::TypeMismatch);
159 }
160 entry.value = value;
161 Ok(())
162 }
163}
164
165#[cfg(test)]
166mod tests {
167 use super::*;
168 use crate::datatypes::Value;
169
170 fn od() -> ObjectDictionary<8> {
171 let mut od = ObjectDictionary::new();
172 od.insert(
174 Address::new(0x1000, 0),
175 Entry::constant(Value::Unsigned32(0x0000_0192)),
176 )
177 .unwrap();
178 od.insert(Address::new(0x1017, 0), Entry::rw(Value::Unsigned16(1000)))
180 .unwrap();
181 od
182 }
183
184 #[test]
185 fn read_returns_stored_value() {
186 assert_eq!(
187 od().read(Address::new(0x1000, 0)).unwrap(),
188 Value::Unsigned32(0x192)
189 );
190 }
191
192 #[test]
193 fn read_missing_object_errors() {
194 assert_eq!(
195 od().read(Address::new(0x9999, 0)),
196 Err(Error::ObjectNotFound)
197 );
198 }
199
200 #[test]
201 fn write_updates_value() {
202 let mut od = od();
203 od.write(Address::new(0x1017, 0), Value::Unsigned16(500))
204 .unwrap();
205 assert_eq!(
206 od.read(Address::new(0x1017, 0)).unwrap(),
207 Value::Unsigned16(500)
208 );
209 }
210
211 #[test]
212 fn write_read_only_object_errors() {
213 let mut od = od();
214 assert_eq!(
215 od.write(Address::new(0x1000, 0), Value::Unsigned32(1)),
216 Err(Error::ReadOnly)
217 );
218 }
219
220 #[test]
221 fn write_wrong_type_errors() {
222 let mut od = od();
223 assert_eq!(
224 od.write(Address::new(0x1017, 0), Value::Unsigned32(1)),
225 Err(Error::TypeMismatch)
226 );
227 }
228
229 #[test]
230 fn dictionary_full_errors() {
231 let mut od: ObjectDictionary<1> = ObjectDictionary::new();
232 od.insert(Address::new(0x2000, 0), Entry::rw(Value::Unsigned8(1)))
233 .unwrap();
234 assert_eq!(
235 od.insert(Address::new(0x2001, 0), Entry::rw(Value::Unsigned8(2))),
236 Err(Error::DictionaryFull)
237 );
238 }
239}