sml_rs/parser/
common.rs

1//! Types used by both parsers.
2
3pub use super::OctetStr;
4use super::{
5    map, take, take_byte,
6    tlf::{Ty, TypeLengthField},
7    NumberFormatter, OctetStrFormatter, ParseError, ResTy, SmlParse, SmlParseTlf,
8};
9
10#[derive(PartialEq, Eq, Clone)]
11/// `SML_PublicOpen.Res` message
12pub struct OpenResponse<'i> {
13    /// alternative codepage. Defaults to `ISO 8859-15`
14    pub codepage: Option<OctetStr<'i>>,
15    /// identification of the client
16    pub client_id: Option<OctetStr<'i>>,
17    /// identification of the request/response pair
18    pub req_file_id: OctetStr<'i>,
19    /// identification of the server
20    pub server_id: OctetStr<'i>,
21    /// reference time
22    pub ref_time: Option<Time>,
23    /// version of the SML protocol. Defaults to `1`
24    pub sml_version: Option<u8>,
25}
26
27impl<'i> SmlParseTlf<'i> for OpenResponse<'i> {
28    fn check_tlf(tlf: &TypeLengthField) -> bool {
29        *tlf == TypeLengthField::new(Ty::ListOf, 6usize as u32)
30    }
31
32    fn parse_with_tlf(input: &'i [u8], _tlf: &TypeLengthField) -> ResTy<'i, Self> {
33        let (input, codepage) = <Option<OctetStr<'i>>>::parse(input)?;
34        let (input, client_id) = <Option<OctetStr<'i>>>::parse(input)?;
35        let (input, req_file_id) = <OctetStr<'i>>::parse(input)?;
36        let (input, server_id) = <OctetStr<'i>>::parse(input)?;
37        let (input, ref_time) = <Option<Time>>::parse(input)?;
38        let (input, sml_version) = <Option<u8>>::parse(input)?;
39        let val = OpenResponse {
40            codepage,
41            client_id,
42            req_file_id,
43            server_id,
44            ref_time,
45            sml_version,
46        };
47        Ok((input, val))
48    }
49}
50
51impl<'i> core::fmt::Debug for OpenResponse<'i> {
52    fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
53        let mut x = f.debug_struct("OpenResponse");
54        if let Some(e) = &self.codepage {
55            x.field("codepage", &OctetStrFormatter(e));
56        }
57        if let Some(e) = &self.client_id {
58            x.field("client_id", &OctetStrFormatter(e));
59        }
60        x.field("req_file_id", &OctetStrFormatter(self.req_file_id));
61        x.field("server_id", &OctetStrFormatter(self.server_id));
62        if let Some(e) = &self.ref_time {
63            x.field("ref_time", &e);
64        }
65        if let Some(e) = &self.sml_version {
66            x.field("sml_version", &e);
67        }
68        x.finish()
69    }
70}
71
72#[derive(PartialEq, Eq, Clone)]
73/// SML ListEntry type
74pub struct ListEntry<'i> {
75    /// name of the entry
76    pub obj_name: OctetStr<'i>,
77    /// status of the entry, content is unspecified in SML
78    pub status: Option<Status>,
79    /// time when the value was obtained
80    pub val_time: Option<Time>,
81    /// code of the value's unit according to DLMS-Unit-List (see IEC 62056-62)
82    pub unit: Option<Unit>,
83    /// scaler of the value. Calculation: `value = self.value * 10 ^ self.scaler`
84    pub scaler: Option<i8>,
85    /// the raw value. See `scaler` and `unit` for how to interpret the value
86    pub value: Value<'i>,
87    /// signature of the value?!
88    pub value_signature: Option<Signature<'i>>,
89}
90
91impl<'i> SmlParseTlf<'i> for ListEntry<'i> {
92    fn check_tlf(tlf: &TypeLengthField) -> bool {
93        *tlf == TypeLengthField::new(Ty::ListOf, 7usize as u32)
94    }
95
96    fn parse_with_tlf(input: &'i [u8], _tlf: &TypeLengthField) -> ResTy<'i, Self> {
97        let (input, obj_name) = <OctetStr<'i>>::parse(input)?;
98        let (input, status) = <Option<Status>>::parse(input)?;
99        let (input, val_time) = <Option<Time>>::parse(input)?;
100        let (input, unit) = <Option<Unit>>::parse(input)?;
101        let (input, scaler) = <Option<i8>>::parse(input)?;
102        let (input, value) = <Value<'i>>::parse(input)?;
103        let (input, value_signature) = <Option<Signature<'i>>>::parse(input)?;
104        let val = ListEntry {
105            obj_name,
106            status,
107            val_time,
108            unit,
109            scaler,
110            value,
111            value_signature,
112        };
113        Ok((input, val))
114    }
115}
116
117impl<'i> core::fmt::Debug for ListEntry<'i> {
118    fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
119        let mut x = f.debug_struct("ListEntry");
120        x.field("obj_name", &OctetStrFormatter(self.obj_name));
121        if let Some(e) = &self.status {
122            x.field("status", &e);
123        }
124        if let Some(e) = &self.val_time {
125            x.field("val_time", &e);
126        }
127        if let Some(e) = &self.unit {
128            x.field("unit", &e);
129        }
130        if let Some(e) = &self.scaler {
131            x.field("scaler", &e);
132        }
133        x.field("value", &self.value);
134        if let Some(e) = &self.value_signature {
135            x.field("value_signature", &e);
136        }
137        x.finish()
138    }
139}
140
141#[derive(PartialEq, Eq, Clone)]
142/// SML value type
143#[allow(missing_docs)]
144pub enum Value<'i> {
145    Bool(bool),
146    Bytes(OctetStr<'i>),
147    I8(i8),
148    I16(i16),
149    I32(i32),
150    I64(i64),
151    U8(u8),
152    U16(u16),
153    U32(u32),
154    U64(u64),
155    List(ListType),
156}
157
158impl<'i> SmlParseTlf<'i> for Value<'i> {
159    fn check_tlf(_tlf: &TypeLengthField) -> bool {
160        true
161    }
162
163    fn parse_with_tlf(input: &'i [u8], tlf: &TypeLengthField) -> ResTy<'i, Self> {
164        match tlf {
165            tlf if <bool>::check_tlf(tlf) => map(<bool>::parse_with_tlf(input, tlf), Self::Bool),
166            tlf if <OctetStr<'i>>::check_tlf(tlf) => {
167                map(<OctetStr<'i>>::parse_with_tlf(input, tlf), Self::Bytes)
168            }
169            tlf if <i8>::check_tlf(tlf) => map(<i8>::parse_with_tlf(input, tlf), Self::I8),
170            tlf if <i16>::check_tlf(tlf) => map(<i16>::parse_with_tlf(input, tlf), Self::I16),
171            tlf if <i32>::check_tlf(tlf) => map(<i32>::parse_with_tlf(input, tlf), Self::I32),
172            tlf if <i64>::check_tlf(tlf) => map(<i64>::parse_with_tlf(input, tlf), Self::I64),
173            tlf if <u8>::check_tlf(tlf) => map(<u8>::parse_with_tlf(input, tlf), Self::U8),
174            tlf if <u16>::check_tlf(tlf) => map(<u16>::parse_with_tlf(input, tlf), Self::U16),
175            tlf if <u32>::check_tlf(tlf) => map(<u32>::parse_with_tlf(input, tlf), Self::U32),
176            tlf if <u64>::check_tlf(tlf) => map(<u64>::parse_with_tlf(input, tlf), Self::U64),
177            tlf if <ListType>::check_tlf(tlf) => {
178                map(<ListType>::parse_with_tlf(input, tlf), Self::List)
179            }
180            _ => Err(ParseError::TlfMismatch(core::any::type_name::<Self>())),
181        }
182    }
183}
184
185impl<'i> core::fmt::Debug for Value<'i> {
186    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
187        match self {
188            Self::Bool(arg0) => write!(f, "{:?}", arg0),
189            Self::Bytes(arg0) => write!(f, "{:?}", OctetStrFormatter(arg0)),
190            Self::I8(arg0) => write!(f, "{:?}", NumberFormatter(arg0)),
191            Self::I16(arg0) => write!(f, "{:?}", NumberFormatter(arg0)),
192            Self::I32(arg0) => write!(f, "{:?}", NumberFormatter(arg0)),
193            Self::I64(arg0) => write!(f, "{:?}", NumberFormatter(arg0)),
194            Self::U8(arg0) => write!(f, "{:?}", NumberFormatter(arg0)),
195            Self::U16(arg0) => write!(f, "{:?}", NumberFormatter(arg0)),
196            Self::U32(arg0) => write!(f, "{:?}", NumberFormatter(arg0)),
197            Self::U64(arg0) => write!(f, "{:?}", NumberFormatter(arg0)),
198            Self::List(arg0) => write!(f, "{:?}", arg0),
199        }
200    }
201}
202
203#[derive(Debug, PartialEq, Eq, Clone)]
204/// SML ListType type
205pub enum ListType {
206    /// variant containing time information
207    Time(Time),
208}
209
210impl<'i> SmlParseTlf<'i> for ListType {
211    fn check_tlf(tlf: &TypeLengthField) -> bool {
212        tlf.ty == Ty::ListOf && tlf.len == 2
213    }
214
215    fn parse_with_tlf(input: &'i [u8], _tlf: &TypeLengthField) -> ResTy<'i, Self> {
216        let (input, tag) = u8::parse(input)?;
217        match tag {
218            1 => {
219                let (input, x) = <Time>::parse(input)?;
220                Ok((input, ListType::Time(x)))
221            }
222            _ => Err(ParseError::UnexpectedVariant),
223        }
224    }
225}
226
227#[derive(PartialEq, Eq, Clone)]
228/// SML status type. Meaning of status values is not specified in SML.
229pub enum Status {
230    /// `u8` status
231    Status8(u8),
232    /// `u16` status
233    Status16(u16),
234    /// `u32` status
235    Status32(u32),
236    /// `u64` status
237    Status64(u64),
238}
239
240impl<'i> SmlParseTlf<'i> for Status {
241    fn check_tlf(_tlf: &TypeLengthField) -> bool {
242        true
243    }
244
245    fn parse_with_tlf(input: &'i [u8], tlf: &TypeLengthField) -> ResTy<'i, Self> {
246        match tlf {
247            tlf if <u8>::check_tlf(tlf) => map(<u8>::parse_with_tlf(input, tlf), Self::Status8),
248            tlf if <u16>::check_tlf(tlf) => map(<u16>::parse_with_tlf(input, tlf), Self::Status16),
249            tlf if <u32>::check_tlf(tlf) => map(<u32>::parse_with_tlf(input, tlf), Self::Status32),
250            tlf if <u64>::check_tlf(tlf) => map(<u64>::parse_with_tlf(input, tlf), Self::Status64),
251            _ => Err(ParseError::TlfMismatch(core::any::type_name::<Self>())),
252        }
253    }
254}
255
256impl ::core::fmt::Debug for Status {
257    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
258        match self {
259            Self::Status8(x) => write!(f, "{:?}", NumberFormatter(x)),
260            Self::Status16(x) => write!(f, "{:?}", NumberFormatter(x)),
261            Self::Status32(x) => write!(f, "{:?}", NumberFormatter(x)),
262            Self::Status64(x) => write!(f, "{:?}", NumberFormatter(x)),
263        }
264    }
265}
266
267/// unit code according to DLMS-Unit-List (see IEC 62056-62)
268pub type Unit = u8; // proper enum?
269
270#[derive(PartialEq, Eq, Clone)]
271/// `SML_PublicClose.Res` message
272pub struct CloseResponse<'i> {
273    /// optional signature
274    pub global_signature: Option<Signature<'i>>,
275}
276
277impl<'i> SmlParseTlf<'i> for CloseResponse<'i> {
278    fn check_tlf(tlf: &TypeLengthField) -> bool {
279        *tlf == TypeLengthField::new(Ty::ListOf, 1usize as u32)
280    }
281
282    fn parse_with_tlf(input: &'i [u8], _tlf: &TypeLengthField) -> ResTy<'i, Self> {
283        let (input, global_signature) = <Option<Signature<'i>>>::parse(input)?;
284        let val = CloseResponse { global_signature };
285        Ok((input, val))
286    }
287}
288
289impl<'i> core::fmt::Debug for CloseResponse<'i> {
290    fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
291        let mut x = f.debug_struct("CloseResponse");
292        if let Some(e) = &self.global_signature {
293            x.field("global_signature", &e);
294        }
295        x.finish()
296    }
297}
298
299#[derive(Debug, PartialEq, Eq, Clone)]
300pub(crate) struct EndOfSmlMessage;
301
302impl<'i> SmlParse<'i> for EndOfSmlMessage {
303    fn parse(input: &'i [u8]) -> ResTy<Self> {
304        let (input, b) = take_byte(input)?;
305        if b != 0x00 {
306            return Err(ParseError::MsgEndMismatch);
307        }
308        Ok((input, EndOfSmlMessage))
309    }
310}
311
312#[derive(PartialEq, Eq, Clone)]
313/// SML Time type
314pub enum Time {
315    /// usually the number of seconds since the power meter was installed
316    SecIndex(u32),
317}
318
319impl<'i> SmlParseTlf<'i> for Time {
320    fn check_tlf(tlf: &TypeLengthField) -> bool {
321        (tlf.ty == Ty::ListOf && tlf.len == 2) || *tlf == TypeLengthField::new(Ty::Unsigned, 4)
322    }
323
324    fn parse_with_tlf(input: &'i [u8], tlf: &TypeLengthField) -> ResTy<'i, Self> {
325        // Workaround for Holley DTZ541:
326        // For the `Time` type, this meter doesn't respect the spec.
327        // Intead of a TLF of type ListOf and length 2, it directly sends an u32 integer,
328        // which is encoded by a TLF of Unsigned and length 4 followed by four bytes containing
329        // the data.
330        if *tlf == TypeLengthField::new(Ty::Unsigned, 4) {
331            let (input, bytes) = take::<4>(input)?;
332            return Ok((input, Time::SecIndex(u32::from_be_bytes(*bytes))));
333        }
334
335        let (input, tag) = u8::parse(input)?;
336        match tag {
337            1 => {
338                let (input, x) = <u32>::parse(input)?;
339                Ok((input, Time::SecIndex(x)))
340            }
341            _ => Err(ParseError::UnexpectedVariant),
342        }
343    }
344}
345
346impl ::core::fmt::Debug for Time {
347    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
348        match self {
349            Self::SecIndex(arg0) => write!(f, "SecIndex({})", arg0),
350        }
351    }
352}
353
354/// SML signature type
355pub type Signature<'i> = OctetStr<'i>;