1use crate::{data_unit::common::MAX_PDU_DATA_LEN, errors::MbusError};
31#[cfg(all(feature = "defmt-format", target_os = "none"))]
32use defmt;
33use heapless::Vec;
34
35#[derive(Debug, Clone, PartialEq)]
37pub struct DeviceIdObject {
38 pub object_id: ObjectId,
40 pub value: Vec<u8, MAX_PDU_DATA_LEN>,
42}
43
44pub struct DeviceIdObjectIterator<'a> {
49 pub(crate) data: &'a [u8],
51 offset: usize,
53 count: u8,
55 total: u8,
57}
58
59impl<'a> Iterator for DeviceIdObjectIterator<'a> {
60 type Item = Result<DeviceIdObject, MbusError>;
61
62 fn next(&mut self) -> Option<Self::Item> {
64 if self.count >= self.total {
65 return None;
66 }
67
68 self.parse_next()
71 }
72}
73
74impl<'a> DeviceIdObjectIterator<'a> {
75 fn parse_next(&mut self) -> Option<Result<DeviceIdObject, MbusError>> {
82 if self.offset + 2 > self.data.len() {
84 return Some(Err(MbusError::InvalidPduLength));
85 }
86 let obj_id = ObjectId::from(self.data[self.offset]);
87 let obj_len = self.data[self.offset + 1] as usize;
88 self.offset += 2; if self.offset + obj_len > self.data.len() {
92 return Some(Err(MbusError::InvalidPduLength));
93 }
94
95 let mut value = Vec::new();
96 if value
98 .extend_from_slice(&self.data[self.offset..self.offset + obj_len])
99 .is_err()
100 {
101 return Some(Err(MbusError::BufferTooSmall));
102 }
103
104 self.offset += obj_len;
105 self.count += 1;
106
107 Some(Ok(DeviceIdObject {
108 object_id: obj_id,
109 value,
110 }))
111 }
112}
113
114#[derive(Debug, Clone, PartialEq)]
116pub struct DeviceIdentificationResponse {
117 pub read_device_id_code: ReadDeviceIdCode,
119 pub conformity_level: ConformityLevel,
121 pub more_follows: bool,
123 pub next_object_id: ObjectId,
125 pub objects_data: [u8; MAX_PDU_DATA_LEN],
127 pub number_of_objects: u8,
129}
130
131impl DeviceIdentificationResponse {
132 pub fn objects(&self) -> DeviceIdObjectIterator<'_> {
134 DeviceIdObjectIterator {
135 data: &self.objects_data,
136 offset: 0,
137 count: 0,
138 total: self.number_of_objects,
139 }
140 }
141}
142
143#[derive(Debug, Clone, Copy, PartialEq, Eq)]
148#[repr(u8)]
149pub enum BasicObjectId {
150 VendorName = 0x00,
152 ProductCode = 0x01,
154 MajorMinorRevision = 0x02,
156}
157
158impl TryFrom<u8> for BasicObjectId {
159 type Error = MbusError;
160
161 fn try_from(value: u8) -> Result<Self, Self::Error> {
162 match value {
163 0x00 => Ok(BasicObjectId::VendorName),
164 0x01 => Ok(BasicObjectId::ProductCode),
165 0x02 => Ok(BasicObjectId::MajorMinorRevision),
166 _ => Err(MbusError::InvalidAddress),
167 }
168 }
169}
170
171#[cfg(all(feature = "defmt-format", target_os = "none"))]
172impl defmt::Format for BasicObjectId {
173 fn format(&self, f: defmt::Formatter) {
174 match self {
175 BasicObjectId::VendorName => defmt::write!(f, "VendorName"),
176 BasicObjectId::ProductCode => defmt::write!(f, "ProductCode"),
177 BasicObjectId::MajorMinorRevision => defmt::write!(f, "MajorMinorRevision"),
178 }
179 }
180}
181
182#[cfg(feature = "error-trait")]
183impl core::fmt::Display for BasicObjectId {
184 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
185 match self {
186 BasicObjectId::VendorName => write!(f, "VendorName"),
187 BasicObjectId::ProductCode => write!(f, "ProductCode"),
188 BasicObjectId::MajorMinorRevision => write!(f, "MajorMinorRevision"),
189 }
190 }
191}
192
193#[derive(Debug, Clone, Copy, PartialEq, Eq)]
198#[repr(u8)]
199pub enum RegularObjectId {
200 VendorUrl = 0x03,
202 ProductName = 0x04,
204 ModelName = 0x05,
206 UserApplicationName = 0x06,
208}
209
210impl TryFrom<u8> for RegularObjectId {
211 type Error = MbusError;
212
213 fn try_from(value: u8) -> Result<Self, Self::Error> {
214 match value {
215 0x03 => Ok(RegularObjectId::VendorUrl),
216 0x04 => Ok(RegularObjectId::ProductName),
217 0x05 => Ok(RegularObjectId::ModelName),
218 0x06 => Ok(RegularObjectId::UserApplicationName),
219 _ => Err(MbusError::InvalidAddress),
220 }
221 }
222}
223
224#[cfg(all(feature = "defmt-format", target_os = "none"))]
225impl defmt::Format for RegularObjectId {
226 fn format(&self, f: defmt::Formatter) {
227 match self {
228 RegularObjectId::VendorUrl => defmt::write!(f, "VendorUrl"),
229 RegularObjectId::ProductName => defmt::write!(f, "ProductName"),
230 RegularObjectId::ModelName => defmt::write!(f, "ModelName"),
231 RegularObjectId::UserApplicationName => defmt::write!(f, "UserApplicationName"),
232 }
233 }
234}
235
236#[cfg(feature = "error-trait")]
237impl core::fmt::Display for RegularObjectId {
238 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
239 match self {
240 RegularObjectId::VendorUrl => write!(f, "VendorUrl"),
241 RegularObjectId::ProductName => write!(f, "ProductName"),
242 RegularObjectId::ModelName => write!(f, "ModelName"),
243 RegularObjectId::UserApplicationName => write!(f, "UserApplicationName"),
244 }
245 }
246}
247
248#[derive(Debug, Clone, Copy, PartialEq, Eq)]
253pub struct ExtendedObjectId(u8);
254
255impl ExtendedObjectId {
256 pub fn new(id: u8) -> Option<Self> {
260 if (0x80..=0xFF).contains(&id) {
261 Some(Self(id))
262 } else {
263 None
264 }
265 }
266
267 pub fn value(&self) -> u8 {
269 self.0
270 }
271}
272
273#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
277#[repr(u8)]
278pub enum ReadDeviceIdCode {
279 #[default]
282 Err,
283 Basic = 0x01,
285 Regular = 0x02,
287 Extended = 0x03,
289 Specific = 0x04,
291}
292
293impl TryFrom<u8> for ReadDeviceIdCode {
294 type Error = MbusError;
295
296 fn try_from(value: u8) -> Result<Self, Self::Error> {
297 match value {
298 0x01 => Ok(ReadDeviceIdCode::Basic),
299 0x02 => Ok(ReadDeviceIdCode::Regular),
300 0x03 => Ok(ReadDeviceIdCode::Extended),
301 0x04 => Ok(ReadDeviceIdCode::Specific),
302 _ => Err(MbusError::InvalidDeviceIdCode),
303 }
304 }
305}
306
307#[derive(Debug, Clone, Copy, PartialEq, Eq)]
309#[repr(u8)]
310pub enum ConformityLevel {
311 BasicStreamOnly = 0x01,
313 RegularStreamOnly = 0x02,
315 ExtendedStreamOnly = 0x03,
317 BasicStreamAndIndividual = 0x81,
319 RegularStreamAndIndividual = 0x82,
321 ExtendedStreamAndIndividual = 0x83,
323}
324
325impl TryFrom<u8> for ConformityLevel {
326 type Error = MbusError;
327
328 fn try_from(value: u8) -> Result<Self, Self::Error> {
329 match value {
330 0x01 => Ok(ConformityLevel::BasicStreamOnly),
331 0x02 => Ok(ConformityLevel::RegularStreamOnly),
332 0x03 => Ok(ConformityLevel::ExtendedStreamOnly),
333 0x81 => Ok(ConformityLevel::BasicStreamAndIndividual),
334 0x82 => Ok(ConformityLevel::RegularStreamAndIndividual),
335 0x83 => Ok(ConformityLevel::ExtendedStreamAndIndividual),
336 _ => Err(MbusError::ParseError),
337 }
338 }
339}
340
341#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
343pub enum ObjectId {
344 #[default]
347 Err,
348 Basic(BasicObjectId),
350 Regular(RegularObjectId),
352 Extended(ExtendedObjectId),
354 Reserved(u8),
356}
357
358impl From<u8> for ObjectId {
359 fn from(id: u8) -> Self {
360 if let Ok(basic) = BasicObjectId::try_from(id) {
361 ObjectId::Basic(basic)
362 } else if let Ok(regular) = RegularObjectId::try_from(id) {
363 ObjectId::Regular(regular)
364 } else if let Some(extended) = ExtendedObjectId::new(id) {
365 ObjectId::Extended(extended)
366 } else {
367 ObjectId::Reserved(id)
368 }
369 }
370}
371
372#[cfg(all(feature = "defmt-format", target_os = "none"))]
373impl defmt::Format for ObjectId {
374 fn format(&self, f: defmt::Formatter) {
375 match self {
376 ObjectId::Basic(id) => defmt::write!(f, "Basic({})", id),
377 ObjectId::Regular(id) => defmt::write!(f, "Regular({})", id),
378 ObjectId::Extended(id) => defmt::write!(f, "Extended({:#04X})", id.value()),
379 ObjectId::Reserved(id) => defmt::write!(f, "Reserved({:#04X})", id),
380 ObjectId::Err => defmt::write!(f, "Err (sentinel default)"),
381 }
382 }
383}
384
385#[cfg(feature = "error-trait")]
386impl core::fmt::Display for ObjectId {
387 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
388 match self {
389 ObjectId::Basic(id) => write!(f, "Basic({})", id),
390 ObjectId::Regular(id) => write!(f, "Regular({})", id),
391 ObjectId::Extended(id) => write!(f, "Extended({:#04X})", id.value()),
392 ObjectId::Reserved(id) => write!(f, "Reserved({:#04X})", id),
393 ObjectId::Err => write!(f, "Err (sentinel default)"),
394 }
395 }
396}
397
398impl From<ObjectId> for u8 {
399 fn from(oid: ObjectId) -> u8 {
400 match oid {
401 ObjectId::Basic(id) => id as u8,
402 ObjectId::Regular(id) => id as u8,
403 ObjectId::Extended(id) => id.value(),
404 ObjectId::Reserved(id) => id,
405 ObjectId::Err => 0, }
407 }
408}