resol_vbus/
recording_encoder.rs

1//! Functions in the module can be used to convert a `Data` variant into the respective recorded
2//! representation according to the VBus Recording File Format.
3
4use byteorder::{ByteOrder, LittleEndian};
5use chrono::{DateTime, TimeZone, Utc};
6
7use crate::{data::Data, header::Header};
8
9/// Returns the number of bytes that the recorded representation of the Data needs.
10pub fn length_from_data(data: &Data) -> usize {
11    match *data {
12        Data::Packet(ref packet) => 26 + packet.frame_count as usize * 4,
13        Data::Datagram(_) => 26 + 6,
14        Data::Telegram(ref tgram) => 26 + tgram.frame_count() as usize * 7,
15    }
16}
17
18/// Stores the timestamp in the provided byte slice.
19pub fn bytes_from_timestamp(timestamp: DateTime<Utc>, buf: &mut [u8]) {
20    let timestamp_s = timestamp.timestamp();
21    let timestamp_ms = timestamp.timestamp_subsec_millis();
22    let timestamp = timestamp_s * 1000 + i64::from(timestamp_ms);
23
24    LittleEndian::write_i64(&mut buf[0..8], timestamp);
25}
26
27/// Stores the record header in the provided byte slice.
28pub fn bytes_from_record(typ: u8, length: u16, timestamp: DateTime<Utc>, buf: &mut [u8]) {
29    buf[0] = 0xA5;
30    buf[1] = typ;
31    LittleEndian::write_u16(&mut buf[2..4], length);
32    LittleEndian::write_u16(&mut buf[4..6], length);
33    bytes_from_timestamp(timestamp, &mut buf[6..14]);
34}
35
36/// Stores a "VBus channel marker" record in the provided byte slice.
37pub fn bytes_from_channel(channel: u8, buf: &mut [u8]) {
38    bytes_from_record(0x77, 16, Utc.timestamp(0, 0), buf);
39    buf[14] = channel;
40    buf[15] = 0;
41}
42
43/// Stores the recorded representation of the Data in the provided byte slice.
44pub fn bytes_from_data(data: &Data, buf: &mut [u8]) {
45    let length = length_from_data(data);
46
47    {
48        let header: &Header = data.as_ref();
49        bytes_from_record(0x66, length as u16, header.timestamp, buf);
50        LittleEndian::write_u16(&mut buf[14..16], header.destination_address);
51        LittleEndian::write_u16(&mut buf[16..18], header.source_address);
52        buf[18] = header.protocol_version;
53        buf[19] = 0;
54    }
55
56    match *data {
57        Data::Packet(ref packet) => {
58            let frame_data_length = packet.frame_count as usize * 4;
59
60            LittleEndian::write_u16(&mut buf[20..22], packet.command);
61            LittleEndian::write_u16(&mut buf[22..24], frame_data_length as u16);
62            buf[24] = 0;
63            buf[25] = 0;
64            buf[26..(26 + frame_data_length)]
65                .copy_from_slice(&packet.frame_data[0..frame_data_length]);
66        }
67        Data::Datagram(ref dgram) => {
68            LittleEndian::write_u16(&mut buf[20..22], dgram.command);
69            buf[22] = 6;
70            buf[23] = 0;
71            buf[24] = 0;
72            buf[25] = 0;
73            LittleEndian::write_i16(&mut buf[26..28], dgram.param16);
74            LittleEndian::write_i32(&mut buf[28..32], dgram.param32);
75        }
76        Data::Telegram(ref tgram) => {
77            let frame_data_length = tgram.frame_count() as usize * 7;
78
79            buf[20] = tgram.command;
80            buf[21] = 0;
81            LittleEndian::write_u16(&mut buf[22..24], frame_data_length as u16);
82            buf[24] = 0;
83            buf[25] = 0;
84            buf[26..(26 + frame_data_length)]
85                .copy_from_slice(&tgram.frame_data[0..frame_data_length]);
86        }
87    }
88}
89
90#[cfg(test)]
91mod tests {
92    use super::*;
93
94    use chrono::TimeZone;
95
96    use crate::{
97        recording_decoder::data_from_checked_bytes,
98        test_data::{RECORDING_1, RECORDING_3},
99        test_utils::to_hex_string,
100    };
101
102    #[test]
103    fn test_length_from_data() {
104        let channel = 0x11;
105
106        let data1 = data_from_checked_bytes(channel, &RECORDING_1[100..]);
107
108        assert_eq!(134, length_from_data(&data1));
109
110        let data2 = data_from_checked_bytes(channel, &RECORDING_3[0..]);
111
112        assert_eq!(32, length_from_data(&data2));
113
114        // NOTE(daniel): no official Telegram recording found...
115    }
116
117    #[test]
118    fn test_bytes_from_timestamp() {
119        let timestamp = Utc.timestamp(1485688933, 0);
120
121        let mut buf = [0u8; 8];
122
123        bytes_from_timestamp(timestamp, &mut buf);
124        assert_eq!("880af6e959010000", to_hex_string(&buf));
125    }
126
127    #[test]
128    fn test_bytes_from_record() {
129        let timestamp = Utc.timestamp(1485688933, 0);
130
131        let mut buf = [0u8; 14];
132
133        bytes_from_record(0x66, 134, timestamp, &mut buf);
134        assert_eq!("a56686008600880af6e959010000", to_hex_string(&buf));
135    }
136
137    #[test]
138    fn test_bytes_from_channel() {
139        let mut buf = [0u8; 16];
140
141        bytes_from_channel(0x11, &mut buf);
142        assert_eq!("a5771000100000000000000000001100", to_hex_string(&buf));
143    }
144
145    #[test]
146    fn test_bytes_from_data() {
147        let channel = 0x11;
148
149        let mut buf = [0u8; 1024];
150
151        let data1 = data_from_checked_bytes(channel, &RECORDING_1[100..]);
152
153        bytes_from_data(&data1, &mut buf);
154        assert_eq!(&RECORDING_1[100..234], &buf[0..134]);
155
156        let data2 = data_from_checked_bytes(channel, &RECORDING_3[0..]);
157
158        bytes_from_data(&data2, &mut buf);
159        assert_eq!(&RECORDING_3[0..32], &buf[0..32]);
160
161        // NOTE(daniel): no official Telegram recording found...
162    }
163}