Skip to main content

smart_meter/
lib.rs

1#![deny(missing_debug_implementations)]
2
3use std::io::{self, Read};
4use std::fmt;
5
6use dlms_cosem::{Error as DlmsError, Dlms, ObisMap};
7use mbusparse::{Error as MbusError, Telegram};
8
9#[derive(Debug)]
10pub enum Error {
11  Io(io::Error),
12  DecryptionFailed,
13}
14
15impl fmt::Display for Error {
16  fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
17    match self {
18      Self::Io(err) => err.fmt(f),
19      Self::DecryptionFailed => write!(f, "decryption failed"),
20    }
21  }
22}
23
24impl std::error::Error for Error {}
25
26#[derive(Debug)]
27pub struct SmartMeter<R> {
28  reader: R,
29  dlms: Dlms,
30  buffer: Vec<u8>,
31}
32
33impl<R> SmartMeter<R> {
34  pub fn new(reader: R, dlms: Dlms) -> Self {
35    SmartMeter { reader, dlms, buffer: Vec::new() }
36  }
37}
38
39impl<R: Read> Iterator for SmartMeter<R> {
40  type Item = Result<ObisMap, Error>;
41
42  /// Get the next reading.
43  fn next(&mut self) -> Option<Self::Item> {
44    let mut bytes_needed = 0;
45    let mut telegrams_needed = 1;
46
47    'outer: loop {
48      match (&mut self.reader).take(bytes_needed as u64).read_to_end(&mut self.buffer) {
49        Ok(_) => (),
50        Err(err) => return Some(Err(Error::Io(err))),
51      }
52
53      let mut telegrams = Vec::new();
54
55      let mut buffer = self.buffer.as_slice();
56      let mut telegram_1_len = 0;
57      let mut telegrams_len = 0;
58
59      for i in 0..telegrams_needed {
60        match Telegram::parse(buffer) {
61          Ok((next_buffer, telegram)) => {
62            let telegram_len = buffer.len() - next_buffer.len();
63            if i == 0 {
64              telegram_1_len = telegram_len;
65            }
66            telegrams_len += telegram_len;
67            buffer = next_buffer;
68            telegrams.push(telegram);
69          },
70          Err(MbusError::Incomplete(n)) => {
71            bytes_needed = n.map(|b| b.get()).unwrap_or(1);
72            continue 'outer
73          },
74          Err(MbusError::InvalidStartCharacter) => {
75            self.buffer.remove(0);
76            bytes_needed = 0;
77            continue 'outer
78          }
79          Err(MbusError::InvalidFormat | MbusError::ChecksumMismatch) => {
80            // Input is invalid but not incomplete,
81            // so try advancing the buffer.
82            self.buffer.remove(0);
83            bytes_needed = 0;
84            continue 'outer
85          },
86        }
87      }
88      bytes_needed = 0;
89
90      match self.dlms.decrypt(&telegrams) {
91        Ok((_, obis_map)) => {
92          self.buffer.drain(0..telegrams_len);
93          return Some(Ok(obis_map))
94        },
95        Err(DlmsError::Incomplete(n)) => {
96          telegrams_needed += n.map(|t| t.get()).unwrap_or(1);
97        },
98        Err(DlmsError::InvalidFormat | DlmsError::ChecksumMismatch) => {
99          // Other error, continue with next telegram.
100          self.buffer.drain(0..telegram_1_len);
101          telegrams_needed = 1;
102          continue
103        },
104        Err(DlmsError::DecryptionFailed) => {
105          return Some(Err(Error::DecryptionFailed))
106        },
107      }
108    }
109  }
110}