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]
34 pub fn value(&self) -> Option<&DataType<'_>> {
35 self.data.value.as_ref()
36 }
37
38 #[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 #[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 #[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 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}