Skip to main content

m_bus_application_layer/
variable_user_data.rs

1use super::data_information::{self};
2use super::{DataRecords, LongTplHeader};
3
4#[cfg_attr(feature = "serde", derive(serde::Serialize))]
5#[derive(Debug, Clone, Copy, PartialEq)]
6#[cfg_attr(feature = "defmt", derive(defmt::Format))]
7#[non_exhaustive]
8pub enum DataRecordError {
9    DataInformationError(data_information::DataInformationError),
10    InsufficientData,
11}
12
13#[cfg(feature = "std")]
14impl std::fmt::Display for DataRecordError {
15    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
16        match self {
17            DataRecordError::DataInformationError(e) => write!(f, "{}", e),
18            DataRecordError::InsufficientData => write!(f, "Insufficient data"),
19        }
20    }
21}
22
23#[cfg(feature = "std")]
24impl std::error::Error for DataRecordError {}
25
26#[cfg_attr(feature = "serde", derive(serde::Serialize))]
27#[derive(Debug, Clone, Copy, PartialEq)]
28#[cfg_attr(feature = "defmt", derive(defmt::Format))]
29#[non_exhaustive]
30pub enum VariableUserDataError {
31    DataInformationError(DataRecordError),
32}
33
34#[cfg(feature = "std")]
35impl std::fmt::Display for VariableUserDataError {
36    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
37        match self {
38            VariableUserDataError::DataInformationError(e) => write!(f, "{}", e),
39        }
40    }
41}
42
43#[cfg(feature = "std")]
44impl std::error::Error for VariableUserDataError {}
45
46impl From<DataRecordError> for VariableUserDataError {
47    fn from(error: DataRecordError) -> Self {
48        Self::DataInformationError(error)
49    }
50}
51
52impl<'a> From<&'a [u8]> for DataRecords<'a> {
53    fn from(data: &'a [u8]) -> Self {
54        DataRecords::new(data, None)
55    }
56}
57
58impl<'a> From<(&'a [u8], &'a LongTplHeader)> for DataRecords<'a> {
59    fn from((data, fixed_data_header): (&'a [u8], &'a LongTplHeader)) -> Self {
60        DataRecords::new(data, Some(fixed_data_header))
61    }
62}
63
64#[cfg(all(test, feature = "std"))]
65mod tests {
66    use crate::{data_information::DataFieldCoding, data_record::DataRecord};
67
68    #[test]
69    fn test_parse_variable_data_length() {
70        use crate::data_information::DataFieldCoding;
71        use crate::data_information::DataType;
72        use crate::data_information::TextUnit;
73        use crate::DataRecords;
74
75        let data: &[u8] = &[
76            0x0D, 0x06, 0xC1, 0x12, 0x0D, 0x06, 0xD3, 0x12, 0x34, 0x56, 0x0D, 0x06, 0x02, 0x31,
77            0x32, 0x0D, 0x06, 0xE1, 0xFF, 0x0D, 0x06, 0x00,
78        ];
79
80        let records: Vec<DataRecord<'_>> = DataRecords::from(data).flatten().collect();
81
82        assert_eq!(records.len(), 5);
83        {
84            let record = records.first().unwrap();
85            let code = get_data_field_coding(record);
86            assert_eq!(code, DataFieldCoding::VariableLength);
87            let value = record.data.value.clone().unwrap();
88            assert_eq!(value, DataType::Number(12.0))
89        }
90        {
91            let record = records.get(1).unwrap();
92            let code = get_data_field_coding(record);
93            assert_eq!(code, DataFieldCoding::VariableLength);
94            let value = record.data.value.clone().unwrap();
95            assert_eq!(value, DataType::Number(-563412.0))
96        }
97        {
98            let record = records.get(2).unwrap();
99            let code = get_data_field_coding(record);
100            assert_eq!(code, DataFieldCoding::VariableLength);
101            let value = record.data.value.clone().unwrap();
102            assert_eq!(value, DataType::Text(TextUnit::new(&[0x31, 0x32])))
103        }
104        {
105            let record = records.get(3).unwrap();
106            let code = get_data_field_coding(record);
107            assert_eq!(code, DataFieldCoding::VariableLength);
108            let value = record.data.value.clone().unwrap();
109            assert_eq!(value, DataType::Number(-1.0))
110        }
111        {
112            let record = records.get(4).unwrap();
113            let code = get_data_field_coding(record);
114            assert_eq!(code, DataFieldCoding::VariableLength);
115            let value = record.data.value.clone().unwrap();
116            assert_eq!(value, DataType::Text(TextUnit::new(&[])))
117        }
118    }
119
120    #[test]
121    fn test_parse_variable_lossy_data_length() {
122        use crate::data_information::DataFieldCoding;
123        use crate::data_information::DataType;
124        use crate::data_information::TextUnit;
125        use crate::DataRecords;
126
127        let data: &[u8] = &[
128            0x0D, 0x06, 0xE9, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x0D, 0x06,
129            0x00, 0x0D, 0x06, 0xEF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
130            0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x0D, 0x06, 0x00, 0x0D, 0x06, 0xF4, 0xFF, 0xFF, 0xFF,
131            0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
132            0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
133            0xFF, 0x0D, 0x06, 0x00, 0x0D, 0x06, 0xF5, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
134            0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
135            0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
136            0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x0D,
137            0x06, 0x00, 0x0D, 0x06, 0xF6, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
138            0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
139            0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
140            0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
141            0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x0D,
142            0x06, 0x00,
143        ];
144
145        let records: Vec<DataRecord<'_>> = DataRecords::from(data).flatten().collect();
146
147        assert_eq!(records.len(), 10);
148        {
149            let record = records.first().unwrap();
150            let code = get_data_field_coding(record);
151            assert_eq!(code, DataFieldCoding::VariableLength);
152            let value = record.data.value.clone().unwrap();
153            assert_eq!(value, DataType::LossyNumber(-1.0))
154        }
155        {
156            let record = records.get(1).unwrap();
157            let code = get_data_field_coding(record);
158            assert_eq!(code, DataFieldCoding::VariableLength);
159            let value = record.data.value.clone().unwrap();
160            assert_eq!(value, DataType::Text(TextUnit::new(&[])))
161        }
162        {
163            let record = records.get(2).unwrap();
164            let code = get_data_field_coding(record);
165            assert_eq!(code, DataFieldCoding::VariableLength);
166            let value = record.data.value.clone().unwrap();
167            assert_eq!(value, DataType::LossyNumber(-1.0))
168        }
169        {
170            let record = records.get(3).unwrap();
171            let code = get_data_field_coding(record);
172            assert_eq!(code, DataFieldCoding::VariableLength);
173            let value = record.data.value.clone().unwrap();
174            assert_eq!(value, DataType::Text(TextUnit::new(&[])))
175        }
176        {
177            let record = records.get(4).unwrap();
178            let code = get_data_field_coding(record);
179            assert_eq!(code, DataFieldCoding::VariableLength);
180            let value = record.data.value.clone().unwrap();
181            assert_eq!(value, DataType::LossyNumber(-1.0))
182        }
183        {
184            let record = records.get(5).unwrap();
185            let code = get_data_field_coding(record);
186            assert_eq!(code, DataFieldCoding::VariableLength);
187            let value = record.data.value.clone().unwrap();
188            assert_eq!(value, DataType::Text(TextUnit::new(&[])))
189        }
190        {
191            let record = records.get(6).unwrap();
192            let code = get_data_field_coding(record);
193            assert_eq!(code, DataFieldCoding::VariableLength);
194            let value = record.data.value.clone().unwrap();
195            assert_eq!(value, DataType::LossyNumber(-1.0))
196        }
197        {
198            let record = records.get(7).unwrap();
199            let code = get_data_field_coding(record);
200            assert_eq!(code, DataFieldCoding::VariableLength);
201            let value = record.data.value.clone().unwrap();
202            assert_eq!(value, DataType::Text(TextUnit::new(&[])))
203        }
204        {
205            let record = records.get(8).unwrap();
206            let code = get_data_field_coding(record);
207            assert_eq!(code, DataFieldCoding::VariableLength);
208            let value = record.data.value.clone().unwrap();
209            assert_eq!(value, DataType::LossyNumber(-1.0))
210        }
211        {
212            let record = records.get(9).unwrap();
213            let code = get_data_field_coding(record);
214            assert_eq!(code, DataFieldCoding::VariableLength);
215            let value = record.data.value.clone().unwrap();
216            assert_eq!(value, DataType::Text(TextUnit::new(&[])))
217        }
218    }
219
220    #[test]
221    fn test_parse_variable_data() {
222        use crate::DataRecords;
223
224        /* Data block 1: unit 0, storage No 0, no tariff, instantaneous volume, 12565 l (24 bit integer) */
225        /* DIF = 0x03, VIF = 0x13, Value = 0x153100 */
226        let data = &[0x03, 0x13, 0x15, 0x31, 0x00];
227
228        let _result = DataRecords::from(data.as_slice());
229    }
230
231    #[test]
232    fn test_parse_variable_data2() {
233        /* Data block 2: unit 0, storage No 5, no tariff, maximum volume flow, 113 l/h (4 digit BCD) */
234        let _data = &[0x01, 0xFD, 0x1B, 0x00];
235    }
236
237    /*  Out: PlainText : Unit "%RH"  Value:   33.96
238    In: 0x02, 0xFC, 0x03, 0x48, 0x52, 0x25, 0x74, 0x44, 0x0D*/
239    #[cfg(feature = "plaintext-before-extension")]
240    #[test]
241    fn test_parse_variable_data3() {
242        use crate::DataRecords;
243        /* Data block 3: unit 1, storage No 0, tariff 2, instantaneous energy, 218,37 kWh (6 digit BCD) */
244        let data = &[0x02, 0xFC, 0x03, 0x48, 0x52, 0x25, 0x74, 0x44, 0x0D];
245        let _data = DataRecords::try_from(data.as_slice());
246    }
247
248    /*  Out: PlainText : Unit "%RH"  Value:   33.96
249    In: 0x02, 0xFC, 0x03, 0x48, 0x52, 0x25, 0x74, 0x44, 0x0D*/
250    #[cfg(not(feature = "plaintext-before-extension"))]
251    #[test]
252    fn test_parse_variable_data3() {
253        use crate::DataRecords;
254        /* Data block 3: unit 1, storage No 0, tariff 2, instantaneous energy, 218,37 kWh (6 digit BCD) */
255        let data = &[0x02, 0xFC, 0x74, 0x03, 0x48, 0x52, 0x25, 0x44, 0x0D];
256        let _data = DataRecords::from(data.as_slice());
257    }
258
259    const fn _test_parse_variable_data2() {
260        /* Data block 2: unit 0, storage No 5, no tariff, maximum volume flow, 113 l/h (4 digit BCD) */
261        let _data = &[0xDA, 0x02, 0x3B, 0x13, 0x01];
262    }
263
264    const fn _test_parse_variable_data3() {
265        /* Data block 3: unit 1, storage No 0, tariff 2, instantaneous energy, 218,37 kWh (6 digit BCD) */
266        let _data = &[0x8B, 0x60, 0x04, 0x37, 0x18, 0x02];
267    }
268
269    fn get_data_field_coding(record: &DataRecord) -> DataFieldCoding {
270        record
271            .data_record_header
272            .processed_data_record_header
273            .data_information
274            .clone()
275            .unwrap()
276            .data_field_coding
277    }
278}