sma_proto/energymeter/
header.rs

1/******************************************************************************\
2    sma-proto - A SMA Speedwire protocol library
3    Copyright (C) 2024 Max Maisel
4
5    This program is free software: you can redistribute it and/or modify
6    it under the terms of the GNU Affero General Public License as published by
7    the Free Software Foundation, either version 3 of the License, or
8    (at your option) any later version.
9
10    This program is distributed in the hope that it will be useful,
11    but WITHOUT ANY WARRANTY; without even the implied warranty of
12    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13    GNU Affero General Public License for more details.
14
15    You should have received a copy of the GNU Affero General Public License
16    along with this program.  If not, see <https://www.gnu.org/licenses/>.
17\******************************************************************************/
18#[cfg(not(feature = "std"))]
19use core::{
20    clone::Clone,
21    cmp::{Eq, PartialEq},
22    fmt::Debug,
23    prelude::rust_2021::derive,
24    result::Result::Ok,
25};
26
27use byteorder_cursor::{BigEndian, Cursor};
28
29use super::{Result, SmaEndpoint, SmaSerde};
30
31/// SMA energymeter sub-protocol header.
32#[derive(Clone, Debug, Default, Eq, PartialEq)]
33pub struct SmaEmHeader {
34    /// Source endpoint address.
35    pub src: SmaEndpoint,
36    /// Overflowing timestamp in milliseconds.
37    pub timestamp_ms: u32,
38}
39
40impl SmaEmHeader {
41    /// Serialized length of the energymeter sub-protocol header.
42    pub const LENGTH: usize = 10;
43}
44
45impl SmaSerde for SmaEmHeader {
46    fn serialize(&self, buffer: &mut Cursor<&mut [u8]>) -> Result<()> {
47        buffer.check_remaining(Self::LENGTH)?;
48
49        self.src.serialize(buffer)?;
50        buffer.write_u32::<BigEndian>(self.timestamp_ms);
51
52        Ok(())
53    }
54
55    fn deserialize(buffer: &mut Cursor<&[u8]>) -> Result<Self> {
56        buffer.check_remaining(Self::LENGTH)?;
57
58        let src = SmaEndpoint::deserialize(buffer)?;
59        let timestamp_ms = buffer.read_u32::<BigEndian>();
60
61        Ok(Self { src, timestamp_ms })
62    }
63}
64
65#[cfg(test)]
66mod tests {
67    use super::*;
68
69    #[test]
70    fn test_sma_em_header_serialization() {
71        let header = SmaEmHeader {
72            src: SmaEndpoint {
73                susy_id: 0x1234,
74                serial: 0xDEADBEEF,
75            },
76            timestamp_ms: 1_000_000,
77        };
78        let mut buffer = [0u8; SmaEmHeader::LENGTH];
79        let mut cursor = Cursor::new(&mut buffer[..]);
80
81        if let Err(e) = header.serialize(&mut cursor) {
82            panic!("SmaEmHeader serialization failed: {e:?}");
83        }
84
85        #[rustfmt::skip]
86        let expected = [
87            0x12, 0x34,
88            0xDE, 0xAD, 0xBE, 0xEF,
89            0x00, 0x0F, 0x42, 0x40,
90        ];
91        assert_eq!(SmaEmHeader::LENGTH, cursor.position());
92        assert_eq!(expected, buffer);
93    }
94
95    #[test]
96    fn test_sma_em_header_deserialization() {
97        #[rustfmt::skip]
98        let serialized = [
99            0x12, 0x34,
100            0xDE, 0xAD, 0xBE, 0xEF,
101            0x00, 0x0F, 0x42, 0x40,
102        ];
103
104        let expected = SmaEmHeader {
105            src: SmaEndpoint {
106                susy_id: 0x1234,
107                serial: 0xDEADBEEF,
108            },
109            timestamp_ms: 1_000_000,
110        };
111
112        let mut cursor = Cursor::new(&serialized[..]);
113        match SmaEmHeader::deserialize(&mut cursor) {
114            Err(e) => panic!("SmaEmHeader deserialization failed: {e:?}"),
115            Ok(header) => {
116                assert_eq!(expected, header);
117                assert_eq!(SmaEmHeader::LENGTH, cursor.position());
118            }
119        };
120    }
121}