m_bus_parser/user_data/
data_record.rs1use super::{
2 data_information::{Data, DataFieldCoding, DataInformation, DataInformationBlock, DataType},
3 value_information::{ValueInformation, ValueInformationBlock, ValueLabel},
4 variable_user_data::DataRecordError,
5 FixedDataHeader,
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 pub raw_bytes: &'a [u8],
29}
30
31impl DataRecord<'_> {
32 #[must_use]
33 pub fn get_size(&self) -> usize {
34 self.raw_bytes.len()
35 }
36
37 #[cfg(feature = "std")]
38 #[must_use]
39 pub fn data_hex(&self) -> String {
40 let start = self.data_record_header.get_size();
41 let end = self.get_size();
42 self.raw_bytes
43 .get(start..end)
44 .unwrap_or(&[])
45 .iter()
46 .map(|b| format!("{:02X}", b))
47 .collect::<Vec<_>>()
48 .join(" ")
49 }
50}
51
52impl<'a> DataRecord<'a> {
53 fn parse(
54 data: &'a [u8],
55 fixed_data_header: Option<&'a FixedDataHeader>,
56 ) -> Result<Self, DataRecordError> {
57 let data_record_header = DataRecordHeader::try_from(data)?;
58 let mut header_size = data_record_header.get_size();
59 if data_record_header
60 .raw_data_record_header
61 .data_information_block
62 .data_information_field
63 .data
64 == 0x0F
65 {
66 header_size = 0;
67 }
68 if data.len() < header_size {
69 return Err(DataRecordError::InsufficientData);
70 }
71 let offset = header_size;
72 let mut data_out = Data {
73 value: Some(DataType::ManufacturerSpecific(
74 data.get(offset..)
75 .ok_or(DataRecordError::InsufficientData)?,
76 )),
77 size: data.len() - offset,
78 };
79 if data_record_header
80 .raw_data_record_header
81 .value_information_block
82 .is_some()
83 {
84 if let Some(data_info) = &data_record_header
85 .processed_data_record_header
86 .data_information
87 {
88 data_out = data_info.data_field_coding.parse(
89 data.get(offset..)
90 .ok_or(DataRecordError::InsufficientData)?,
91 fixed_data_header,
92 )?;
93 }
94 }
95
96 let mut record_size = data_record_header.get_size() + data_out.get_size();
97 if record_size > data.len() {
98 record_size = data.len();
99 }
100 let raw_bytes = data
101 .get(..record_size)
102 .ok_or(DataRecordError::InsufficientData)?;
103
104 Ok(DataRecord {
105 data_record_header,
106 data: data_out,
107 raw_bytes,
108 })
109 }
110}
111
112#[cfg_attr(feature = "serde", derive(serde::Serialize))]
113#[derive(Debug, PartialEq, Clone)]
114#[cfg_attr(feature = "defmt", derive(defmt::Format))]
115pub struct DataRecordHeader<'a> {
116 pub raw_data_record_header: RawDataRecordHeader<'a>,
117 pub processed_data_record_header: ProcessedDataRecordHeader,
118}
119
120impl DataRecordHeader<'_> {
121 #[must_use]
122 pub fn get_size(&self) -> usize {
123 let s = self
124 .raw_data_record_header
125 .data_information_block
126 .get_size();
127 if let Some(x) = &self.raw_data_record_header.value_information_block {
128 s + x.get_size()
129 } else {
130 s
131 }
132 }
133}
134
135impl<'a> TryFrom<&'a [u8]> for RawDataRecordHeader<'a> {
136 type Error = DataRecordError;
137 fn try_from(data: &[u8]) -> Result<RawDataRecordHeader, DataRecordError> {
138 let difb = DataInformationBlock::try_from(data)?;
139 let offset = difb.get_size();
140
141 let mut vifb = None;
142
143 if difb.data_information_field.data != 0x0F {
144 vifb = Some(ValueInformationBlock::try_from(
145 data.get(offset..)
146 .ok_or(DataRecordError::InsufficientData)?,
147 )?);
148 }
149
150 Ok(RawDataRecordHeader {
151 data_information_block: difb,
152 value_information_block: vifb,
153 })
154 }
155}
156
157impl TryFrom<&RawDataRecordHeader<'_>> for ProcessedDataRecordHeader {
158 type Error = DataRecordError;
159 fn try_from(raw_data_record_header: &RawDataRecordHeader) -> Result<Self, DataRecordError> {
160 let mut value_information = None;
161 let mut data_information = None;
162
163 if let Some(x) = &raw_data_record_header.value_information_block {
164 let v = ValueInformation::try_from(x)?;
165
166 let mut d = DataInformation::try_from(&raw_data_record_header.data_information_block)?;
167
168 if v.labels.contains(&ValueLabel::Date) {
172 d.data_field_coding = DataFieldCoding::DateTypeG;
173 } else if v.labels.contains(&ValueLabel::DateTime) {
174 d.data_field_coding = DataFieldCoding::DateTimeTypeF;
175 } else if v.labels.contains(&ValueLabel::Time) {
176 d.data_field_coding = DataFieldCoding::DateTimeTypeJ;
177 } else if v.labels.contains(&ValueLabel::DateTimeWithSeconds) {
178 d.data_field_coding = DataFieldCoding::DateTimeTypeI;
179 }
180
181 value_information = Some(v);
182 data_information = Some(d);
183 }
184
185 Ok(Self {
186 data_information,
187 value_information,
188 })
189 }
190}
191
192impl<'a> TryFrom<&'a [u8]> for DataRecordHeader<'a> {
193 type Error = DataRecordError;
194 fn try_from(data: &'a [u8]) -> Result<Self, DataRecordError> {
195 let raw_data_record_header = RawDataRecordHeader::try_from(data)?;
196 let processed_data_record_header =
197 ProcessedDataRecordHeader::try_from(&raw_data_record_header)?;
198 Ok(Self {
199 raw_data_record_header,
200 processed_data_record_header,
201 })
202 }
203}
204
205impl<'a> TryFrom<(&'a [u8], &'a FixedDataHeader)> for DataRecord<'a> {
206 type Error = DataRecordError;
207 fn try_from(
208 (data, fixed_data_header): (&'a [u8], &'a FixedDataHeader),
209 ) -> Result<Self, Self::Error> {
210 Self::parse(data, Some(fixed_data_header))
211 }
212}
213
214impl<'a> TryFrom<&'a [u8]> for DataRecord<'a> {
215 type Error = DataRecordError;
216 fn try_from(data: &'a [u8]) -> Result<Self, Self::Error> {
217 Self::parse(data, None)
218 }
219}
220
221#[cfg(test)]
222mod tests {
223 use super::*;
224
225 #[test]
226 fn test_parse_raw_data_record() {
227 let data = &[0x03, 0x13, 0x15, 0x31, 0x00];
228 let _result = DataRecordHeader::try_from(data.as_slice());
229 }
230 #[test]
231 #[cfg(feature = "std")]
232 fn test_manufacturer_specific_block() {
233 let data = [0x0F, 0x01, 0x02, 0x03, 0x04];
234 let result = DataRecord::try_from(data.as_slice());
235 println!("{:?}", result);
236 }
237}