1use super::{
2 data_information::{
3 Data, DataFieldCoding, DataInformation, DataInformationBlock, DataType, SpecialFunctions,
4 },
5 value_information::{ValueInformation, ValueInformationBlock},
6 variable_user_data::DataRecordError,
7 LongTplHeader,
8};
9#[cfg_attr(feature = "serde", derive(serde::Serialize))]
10#[derive(Debug, PartialEq, Clone)]
11#[cfg_attr(feature = "defmt", derive(defmt::Format))]
12pub struct RawDataRecordHeader<'a> {
13 pub data_information_block: DataInformationBlock<'a>,
14 pub value_information_block: Option<ValueInformationBlock<'a>>,
15}
16#[cfg_attr(feature = "serde", derive(serde::Serialize))]
17#[derive(Debug, PartialEq, Clone)]
18#[cfg_attr(feature = "defmt", derive(defmt::Format))]
19pub struct ProcessedDataRecordHeader<'a> {
20 pub data_information: Option<DataInformation>,
21 pub value_information: Option<ValueInformation<'a>>,
22}
23#[cfg_attr(feature = "serde", derive(serde::Serialize))]
24#[derive(Debug, PartialEq, Clone)]
25#[cfg_attr(feature = "defmt", derive(defmt::Format))]
26pub struct DataRecord<'a> {
27 pub data_record_header: DataRecordHeader<'a>,
28 pub data: Data<'a>,
29 #[cfg_attr(
31 feature = "serde",
32 serde(serialize_with = "m_bus_core::serde_hex::serialize")
33 )]
34 pub raw_bytes: &'a [u8],
35}
36
37impl<'a> DataRecord<'a> {
38 #[must_use]
40 pub fn value(&self) -> Option<&DataType<'_>> {
41 self.data.value.as_ref()
42 }
43
44 #[must_use]
46 pub fn data_information(&self) -> Option<&DataInformation> {
47 self.data_record_header
48 .processed_data_record_header
49 .data_information
50 .as_ref()
51 }
52
53 #[must_use]
55 pub fn value_information(&self) -> Option<&ValueInformation<'a>> {
56 self.data_record_header
57 .processed_data_record_header
58 .value_information
59 .as_ref()
60 }
61
62 #[must_use]
64 pub fn raw_bytes(&self) -> &[u8] {
65 self.raw_bytes
66 }
67
68 #[must_use]
69 pub fn get_size(&self) -> usize {
70 self.raw_bytes.len()
71 }
72
73 #[cfg(feature = "std")]
74 #[must_use]
75 pub fn data_record_header_hex(&self) -> String {
76 let start = 0;
77 let end = self.data_record_header.get_size();
78 self.raw_bytes
79 .get(start..end)
80 .unwrap_or(&[])
81 .iter()
82 .map(|b| format!("{:02X}", b))
83 .collect::<Vec<_>>()
84 .join(" ")
85 }
86
87 #[cfg(feature = "std")]
88 #[must_use]
89 pub fn data_hex(&self) -> String {
90 let start = self.data_record_header.get_size();
91 let end = self.get_size();
92 self.raw_bytes
93 .get(start..end)
94 .unwrap_or(&[])
95 .iter()
96 .map(|b| format!("{:02X}", b))
97 .collect::<Vec<_>>()
98 .join(" ")
99 }
100}
101
102impl<'a> DataRecord<'a> {
103 pub(crate) fn parse(
109 data: &'a [u8],
110 fixed_data_header: Option<&'a LongTplHeader>,
111 consumed: &mut usize,
112 ) -> Result<Self, DataRecordError> {
113 *consumed = data.len();
114 let raw = RawDataRecordHeader::try_from(data);
118 let raw_data_record_header = match &raw {
119 Ok(header) => header,
120 Err(error) => return Err(*error),
121 };
122 let processed = ProcessedDataRecordHeader::try_from(raw_data_record_header);
123 let processed_data_record_header = match &processed {
124 Ok(header) => header,
125 Err(error) => return Err(*error),
126 };
127 let header_size = raw_data_record_header.get_size();
128 let rest = data
129 .get(header_size..)
130 .ok_or(DataRecordError::InsufficientData)?;
131 let data_field_coding = processed_data_record_header
134 .data_information
135 .as_ref()
136 .map_or(
137 DataFieldCoding::SpecialFunctions(SpecialFunctions::ManufacturerSpecific),
138 |data_info| data_info.data_field_coding,
139 );
140 let data_out = match data_field_coding.parse(rest, fixed_data_header) {
141 Ok(data_out) => data_out,
142 Err(error) => {
143 if let Some(size) = data_field_coding
146 .data_size(rest)
147 .and_then(|size| size.checked_add(header_size))
148 .filter(|&size| size != 0 && size <= data.len())
149 {
150 *consumed = size;
151 }
152 return Err(error);
153 }
154 };
155 let raw_bytes = data
157 .get(..header_size + data_out.get_size())
158 .unwrap_or(data);
159 *consumed = raw_bytes.len();
160
161 Ok(Self::assemble(
162 raw_data_record_header,
163 processed_data_record_header,
164 data_out,
165 raw_bytes,
166 ))
167 }
168
169 #[inline(never)]
173 fn assemble(
174 raw_data_record_header: &RawDataRecordHeader<'a>,
175 processed_data_record_header: &ProcessedDataRecordHeader<'a>,
176 data: Data<'a>,
177 raw_bytes: &'a [u8],
178 ) -> Self {
179 DataRecord {
180 data_record_header: DataRecordHeader {
181 raw_data_record_header: raw_data_record_header.clone(),
182 processed_data_record_header: processed_data_record_header.clone(),
183 },
184 data,
185 raw_bytes,
186 }
187 }
188}
189
190#[cfg_attr(feature = "serde", derive(serde::Serialize))]
191#[derive(Debug, PartialEq, Clone)]
192#[cfg_attr(feature = "defmt", derive(defmt::Format))]
193pub struct DataRecordHeader<'a> {
194 pub raw_data_record_header: RawDataRecordHeader<'a>,
195 pub processed_data_record_header: ProcessedDataRecordHeader<'a>,
196}
197
198impl DataRecordHeader<'_> {
199 #[must_use]
200 pub fn get_size(&self) -> usize {
201 self.raw_data_record_header.get_size()
202 }
203}
204
205impl RawDataRecordHeader<'_> {
206 pub(crate) fn get_size(&self) -> usize {
207 let s = self.data_information_block.get_size();
208 if let Some(x) = &self.value_information_block {
209 s + x.get_size()
210 } else {
211 s
212 }
213 }
214}
215
216impl<'a> TryFrom<&'a [u8]> for RawDataRecordHeader<'a> {
217 type Error = DataRecordError;
218 #[inline(never)]
221 fn try_from(data: &[u8]) -> Result<RawDataRecordHeader<'_>, DataRecordError> {
222 let difb = DataInformationBlock::try_from(data)?;
223 let offset = difb.get_size();
224
225 let mut vifb = None;
226
227 if !difb.data_information_field.is_special_function() {
228 vifb = Some(ValueInformationBlock::try_from(
229 data.get(offset..)
230 .ok_or(DataRecordError::InsufficientData)?,
231 )?);
232 }
233
234 Ok(RawDataRecordHeader {
235 data_information_block: difb,
236 value_information_block: vifb,
237 })
238 }
239}
240
241impl<'a> TryFrom<&RawDataRecordHeader<'a>> for ProcessedDataRecordHeader<'a> {
242 type Error = DataRecordError;
243 fn try_from(raw_data_record_header: &RawDataRecordHeader<'a>) -> Result<Self, DataRecordError> {
244 let value_information = match &raw_data_record_header.value_information_block {
245 Some(x) => Some(ValueInformation::try_from(x)?),
246 None if raw_data_record_header
247 .data_information_block
248 .data_information_field
249 .is_special_function() =>
250 {
251 None
252 }
253 None => {
254 return Ok(Self {
255 data_information: None,
256 value_information: None,
257 })
258 }
259 };
260
261 let mut data_information =
264 DataInformation::try_from(&raw_data_record_header.data_information_block);
265 let d = match &mut data_information {
266 Ok(d) => d,
267 Err(error) => return Err((*error).into()),
268 };
269 d.data_field_coding = raw_data_record_header
271 .value_information_block
272 .as_ref()
273 .map_or(d.data_field_coding, |vib| {
274 date_time_coding(vib.value_information.data, d.data_field_coding)
275 });
276
277 Ok(Self {
278 data_information: data_information.ok(),
279 value_information,
280 })
281 }
282}
283
284fn date_time_coding(vif: u8, coding: DataFieldCoding) -> DataFieldCoding {
288 match vif & 0x7F {
289 0x6C => DataFieldCoding::DateTypeG,
290 0x6D if coding == DataFieldCoding::Integer48Bit => DataFieldCoding::DateTimeTypeI,
292 0x6D => DataFieldCoding::DateTimeTypeF,
293 _ => coding,
294 }
295}
296
297impl<'a> TryFrom<&'a [u8]> for DataRecordHeader<'a> {
298 type Error = DataRecordError;
299 fn try_from(data: &'a [u8]) -> Result<Self, DataRecordError> {
300 let raw_data_record_header = RawDataRecordHeader::try_from(data)?;
301 let processed_data_record_header =
302 ProcessedDataRecordHeader::try_from(&raw_data_record_header)?;
303 Ok(Self {
304 raw_data_record_header,
305 processed_data_record_header,
306 })
307 }
308}
309
310impl<'a> TryFrom<(&'a [u8], &'a LongTplHeader)> for DataRecord<'a> {
311 type Error = DataRecordError;
312 fn try_from(
313 (data, fixed_data_header): (&'a [u8], &'a LongTplHeader),
314 ) -> Result<Self, Self::Error> {
315 Self::parse(data, Some(fixed_data_header), &mut 0)
316 }
317}
318
319impl<'a> TryFrom<&'a [u8]> for DataRecord<'a> {
320 type Error = DataRecordError;
321 fn try_from(data: &'a [u8]) -> Result<Self, Self::Error> {
322 Self::parse(data, None, &mut 0)
323 }
324}
325
326#[cfg(test)]
327mod tests {
328 use super::*;
329 use crate::value_information::ValueLabel;
330
331 #[test]
332 fn date_time_overrides_match_all_single_extension_vifs() {
333 for dif in [0x02, 0x04, 0x06, 0x09] {
336 for vif in 0..=u8::MAX {
337 if vif == 0x7d {
340 continue;
341 }
342 for vife in 0..=0x7f {
343 let bytes = [dif, vif, vife];
344 let Ok(raw) = RawDataRecordHeader::try_from(bytes.as_slice()) else {
345 continue;
346 };
347 let Ok(actual) = ProcessedDataRecordHeader::try_from(&raw) else {
348 continue;
349 };
350 let Some(value) = actual.value_information.as_ref() else {
351 continue;
352 };
353 let original = DataInformation::try_from(&raw.data_information_block)
354 .unwrap()
355 .data_field_coding;
356 let expected = if value.has_label(ValueLabel::Date) {
357 DataFieldCoding::DateTypeG
358 } else if value.has_label(ValueLabel::DateTime) {
359 if original == DataFieldCoding::Integer48Bit {
360 DataFieldCoding::DateTimeTypeI
361 } else {
362 DataFieldCoding::DateTimeTypeF
363 }
364 } else if value.has_label(ValueLabel::Time) {
365 DataFieldCoding::DateTimeTypeJ
366 } else if value.has_label(ValueLabel::DateTimeWithSeconds) {
367 DataFieldCoding::DateTimeTypeI
368 } else {
369 original
370 };
371 assert_eq!(
372 actual.data_information.unwrap().data_field_coding,
373 expected,
374 "DIF={dif:02x} VIF={vif:02x} VIFE={vife:02x}",
375 );
376 }
377 }
378 }
379 }
380
381 #[test]
382 fn test_parse_raw_data_record() {
383 let data = &[0x03, 0x13, 0x15, 0x31, 0x00];
384 let _result = DataRecordHeader::try_from(data.as_slice());
385 }
386 #[test]
387 #[cfg(feature = "std")]
388 fn test_manufacturer_specific_block() {
389 let data = [0x0F, 0x01, 0x02, 0x03, 0x04];
390 let result = DataRecord::try_from(data.as_slice());
391 println!("{:?}", result);
392 }
393}