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