m_bus_parser/user_data/
variable_user_data.rs

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