Skip to main content

m_bus_application_layer/
data_record.rs

1use super::{
2    data_information::{Data, DataFieldCoding, DataInformation, DataInformationBlock, DataType},
3    value_information::{ValueInformation, ValueInformationBlock, ValueLabel},
4    variable_user_data::DataRecordError,
5    LongTplHeader,
6};
7#[cfg_attr(feature = "serde", derive(serde::Serialize))]
8#[derive(Debug, PartialEq, Clone)]
9#[cfg_attr(feature = "defmt", derive(defmt::Format))]
10pub struct RawDataRecordHeader<'a> {
11    pub data_information_block: DataInformationBlock<'a>,
12    pub value_information_block: Option<ValueInformationBlock>,
13}
14#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
15#[derive(Debug, PartialEq, Clone)]
16#[cfg_attr(feature = "defmt", derive(defmt::Format))]
17pub struct ProcessedDataRecordHeader {
18    pub data_information: Option<DataInformation>,
19    pub value_information: Option<ValueInformation>,
20}
21#[cfg_attr(feature = "serde", derive(serde::Serialize))]
22#[derive(Debug, PartialEq, Clone)]
23#[cfg_attr(feature = "defmt", derive(defmt::Format))]
24pub struct DataRecord<'a> {
25    pub data_record_header: DataRecordHeader<'a>,
26    pub data: Data<'a>,
27    /// Raw bytes encompassing this data record
28    pub raw_bytes: &'a [u8],
29}
30
31impl DataRecord<'_> {
32    /// Returns the parsed value carried by this record.
33    #[must_use]
34    pub fn value(&self) -> Option<&DataType<'_>> {
35        self.data.value.as_ref()
36    }
37
38    /// Returns the processed data information (DIF and DIFE fields).
39    #[must_use]
40    pub fn data_information(&self) -> Option<&DataInformation> {
41        self.data_record_header
42            .processed_data_record_header
43            .data_information
44            .as_ref()
45    }
46
47    /// Returns the processed value information (VIF and VIFE fields).
48    #[must_use]
49    pub fn value_information(&self) -> Option<&ValueInformation> {
50        self.data_record_header
51            .processed_data_record_header
52            .value_information
53            .as_ref()
54    }
55
56    /// Returns all raw bytes consumed by this record.
57    #[must_use]
58    pub fn raw_bytes(&self) -> &[u8] {
59        self.raw_bytes
60    }
61
62    #[must_use]
63    pub fn get_size(&self) -> usize {
64        self.raw_bytes.len()
65    }
66
67    #[cfg(feature = "std")]
68    #[must_use]
69    pub fn data_record_header_hex(&self) -> String {
70        let start = 0;
71        let end = self.data_record_header.get_size();
72        self.raw_bytes
73            .get(start..end)
74            .unwrap_or(&[])
75            .iter()
76            .map(|b| format!("{:02X}", b))
77            .collect::<Vec<_>>()
78            .join(" ")
79    }
80
81    #[cfg(feature = "std")]
82    #[must_use]
83    pub fn data_hex(&self) -> String {
84        let start = self.data_record_header.get_size();
85        let end = self.get_size();
86        self.raw_bytes
87            .get(start..end)
88            .unwrap_or(&[])
89            .iter()
90            .map(|b| format!("{:02X}", b))
91            .collect::<Vec<_>>()
92            .join(" ")
93    }
94}
95
96impl<'a> DataRecord<'a> {
97    fn parse(
98        data: &'a [u8],
99        fixed_data_header: Option<&'a LongTplHeader>,
100    ) -> Result<Self, DataRecordError> {
101        let data_record_header = DataRecordHeader::try_from(data)?;
102        let header_size = data_record_header.get_size();
103        if data.len() < header_size {
104            return Err(DataRecordError::InsufficientData);
105        }
106        let offset = header_size;
107        let data_out = if let Some(data_info) = &data_record_header
108            .processed_data_record_header
109            .data_information
110        {
111            data_info.data_field_coding.parse(
112                data.get(offset..)
113                    .ok_or(DataRecordError::InsufficientData)?,
114                fixed_data_header,
115            )?
116        } else {
117            Data {
118                value: Some(DataType::ManufacturerSpecific(
119                    data.get(offset..)
120                        .ok_or(DataRecordError::InsufficientData)?,
121                )),
122                size: data.len() - offset,
123            }
124        };
125
126        let mut record_size = data_record_header.get_size() + data_out.get_size();
127        if record_size > data.len() {
128            record_size = data.len();
129        }
130        let raw_bytes = data
131            .get(..record_size)
132            .ok_or(DataRecordError::InsufficientData)?;
133
134        Ok(DataRecord {
135            data_record_header,
136            data: data_out,
137            raw_bytes,
138        })
139    }
140}
141
142#[cfg_attr(feature = "serde", derive(serde::Serialize))]
143#[derive(Debug, PartialEq, Clone)]
144#[cfg_attr(feature = "defmt", derive(defmt::Format))]
145pub struct DataRecordHeader<'a> {
146    pub raw_data_record_header: RawDataRecordHeader<'a>,
147    pub processed_data_record_header: ProcessedDataRecordHeader,
148}
149
150impl DataRecordHeader<'_> {
151    #[must_use]
152    pub fn get_size(&self) -> usize {
153        let s = self
154            .raw_data_record_header
155            .data_information_block
156            .get_size();
157        if let Some(x) = &self.raw_data_record_header.value_information_block {
158            s + x.get_size()
159        } else {
160            s
161        }
162    }
163}
164
165impl<'a> TryFrom<&'a [u8]> for RawDataRecordHeader<'a> {
166    type Error = DataRecordError;
167    fn try_from(data: &[u8]) -> Result<RawDataRecordHeader<'_>, DataRecordError> {
168        let difb = DataInformationBlock::try_from(data)?;
169        let offset = difb.get_size();
170
171        let mut vifb = None;
172
173        if !difb.data_information_field.is_special_function() {
174            vifb = Some(ValueInformationBlock::try_from(
175                data.get(offset..)
176                    .ok_or(DataRecordError::InsufficientData)?,
177            )?);
178        }
179
180        Ok(RawDataRecordHeader {
181            data_information_block: difb,
182            value_information_block: vifb,
183        })
184    }
185}
186
187impl TryFrom<&RawDataRecordHeader<'_>> for ProcessedDataRecordHeader {
188    type Error = DataRecordError;
189    fn try_from(raw_data_record_header: &RawDataRecordHeader) -> Result<Self, DataRecordError> {
190        let mut value_information = None;
191        let mut data_information = None;
192
193        if let Some(x) = &raw_data_record_header.value_information_block {
194            let v = ValueInformation::try_from(x)?;
195
196            let mut d = DataInformation::try_from(&raw_data_record_header.data_information_block)?;
197
198            // unfortunately, the data field coding is not always set in the data information block
199            // so we must do some additional checks to determine the correct data field coding
200
201            if v.labels.contains(&ValueLabel::Date) {
202                d.data_field_coding = DataFieldCoding::DateTypeG;
203            } else if v.labels.contains(&ValueLabel::DateTime) {
204                d.data_field_coding = DataFieldCoding::DateTimeTypeF;
205            } else if v.labels.contains(&ValueLabel::Time) {
206                d.data_field_coding = DataFieldCoding::DateTimeTypeJ;
207            } else if v.labels.contains(&ValueLabel::DateTimeWithSeconds) {
208                d.data_field_coding = DataFieldCoding::DateTimeTypeI;
209            }
210
211            value_information = Some(v);
212            data_information = Some(d);
213        } else if raw_data_record_header
214            .data_information_block
215            .data_information_field
216            .is_special_function()
217        {
218            data_information = Some(DataInformation::try_from(
219                &raw_data_record_header.data_information_block,
220            )?);
221        }
222
223        Ok(Self {
224            data_information,
225            value_information,
226        })
227    }
228}
229
230impl<'a> TryFrom<&'a [u8]> for DataRecordHeader<'a> {
231    type Error = DataRecordError;
232    fn try_from(data: &'a [u8]) -> Result<Self, DataRecordError> {
233        let raw_data_record_header = RawDataRecordHeader::try_from(data)?;
234        let processed_data_record_header =
235            ProcessedDataRecordHeader::try_from(&raw_data_record_header)?;
236        Ok(Self {
237            raw_data_record_header,
238            processed_data_record_header,
239        })
240    }
241}
242
243impl<'a> TryFrom<(&'a [u8], &'a LongTplHeader)> for DataRecord<'a> {
244    type Error = DataRecordError;
245    fn try_from(
246        (data, fixed_data_header): (&'a [u8], &'a LongTplHeader),
247    ) -> Result<Self, Self::Error> {
248        Self::parse(data, Some(fixed_data_header))
249    }
250}
251
252impl<'a> TryFrom<&'a [u8]> for DataRecord<'a> {
253    type Error = DataRecordError;
254    fn try_from(data: &'a [u8]) -> Result<Self, Self::Error> {
255        Self::parse(data, None)
256    }
257}
258
259#[cfg(test)]
260mod tests {
261    use super::*;
262
263    #[test]
264    fn test_parse_raw_data_record() {
265        let data = &[0x03, 0x13, 0x15, 0x31, 0x00];
266        let _result = DataRecordHeader::try_from(data.as_slice());
267    }
268    #[test]
269    #[cfg(feature = "std")]
270    fn test_manufacturer_specific_block() {
271        let data = [0x0F, 0x01, 0x02, 0x03, 0x04];
272        let result = DataRecord::try_from(data.as_slice());
273        println!("{:?}", result);
274    }
275}