1use crate::{E2EError, E2EProfile, E2EResult, E2EStatus};
13use crc::{Crc, CRC_16_IBM_3740};
14
15const BITS_PER_BYTE: u16 = 8;
17const COUNTER_MAX: u8 = 0xFF;
18const COUNTER_MODULO: u16 = 0x100;
19
20#[derive(Debug, Clone)]
22pub struct Profile6Config {
23 pub data_id: u16,
25 pub offset: u16,
27 pub min_data_length: u16,
29 pub max_data_length: u16,
31 pub max_delta_counter: u8,
33}
34
35#[derive(Debug, Clone)]
37pub struct Profile6Check {
38 rx_data_length: u16,
39 rx_counter: u8,
40 rx_crc: u16,
41 calculated_crc: u16,
42 data_len: u16,
43}
44
45impl Default for Profile6Config {
46 fn default() -> Self {
47 Self {
48 data_id: 0x1234,
49 offset: 0x0000,
50 min_data_length: 40, max_data_length: 32768, max_delta_counter: 1,
53 }
54 }
55}
56
57#[derive(Clone)]
61pub struct Profile6 {
62 config: Profile6Config,
63 counter: u8,
64 initialized: bool,
65}
66
67impl Profile6 {
68 fn validate_config(config: &Profile6Config) -> E2EResult<()> {
70 if config.min_data_length < 5 * BITS_PER_BYTE
71 || 4096 * BITS_PER_BYTE < config.min_data_length
72 {
73 return Err(E2EError::InvalidConfiguration(
74 "Minimum Data length shall be between 5B and 4096B".into(),
75 ));
76 }
77 if config.max_data_length < config.min_data_length || 4096 * 8 < config.max_data_length {
78 return Err(E2EError::InvalidConfiguration(
79 "Maximum Data length shall be between MinDataLength and 4096B".into(),
80 ));
81 }
82 if config.max_delta_counter == 0 || config.max_delta_counter == COUNTER_MAX {
83 return Err(E2EError::InvalidConfiguration(format!(
84 "Max delta counter must be between 1 and {}",
85 COUNTER_MAX
86 )));
87 }
88 Ok(())
89 }
90 fn validate_length(&self, len: u16) -> E2EResult<()> {
92 let min_bytes = self.config.min_data_length / BITS_PER_BYTE;
93 let max_bytes = self.config.max_data_length / BITS_PER_BYTE;
94 if len < min_bytes || max_bytes < len {
95 return Err(E2EError::InvalidDataFormat(format!(
96 "Expected {} - {} bytes, got {} bytes",
97 min_bytes, max_bytes, len
98 )));
99 }
100 Ok(())
101 }
102 fn write_data_length(&self, data: &mut [u8]) {
103 let offset = (self.config.offset / BITS_PER_BYTE) as usize;
104 let len16 = data.len() as u16;
105 data[offset + 2..=offset + 3].copy_from_slice(&len16.to_be_bytes());
106 }
107 fn write_counter(&self, data: &mut [u8]) {
108 let offset = (self.config.offset / BITS_PER_BYTE) as usize;
109 data[offset + 4] = self.counter;
110 }
111 fn compute_crc(&self, data: &[u8]) -> u16 {
112 let crc: Crc<u16> = Crc::<u16>::new(&CRC_16_IBM_3740);
113 let mut digest = crc.digest();
114 let offset = (self.config.offset / BITS_PER_BYTE) as usize;
115 digest.update(&data[0..offset]); digest.update(&data[(offset + 2)..]); digest.update(&self.config.data_id.to_be_bytes());
118 digest.finalize()
119 }
120 fn write_crc(&self, calculated_crc: u16, data: &mut [u8]) {
121 let offset = (self.config.offset / BITS_PER_BYTE) as usize;
122 data[offset..=offset + 1].copy_from_slice(&calculated_crc.to_be_bytes());
123 }
124 fn increment_counter(&mut self) {
125 self.counter = (self.counter as u16 + 1) as u8 & COUNTER_MAX;
126 }
127
128 fn read_data_length(&self, data: &[u8]) -> u16 {
129 let offset = (self.config.offset / BITS_PER_BYTE) as usize;
130 u16::from_be_bytes([data[offset + 2], data[offset + 3]])
131 }
132 fn read_counter(&self, data: &[u8]) -> u8 {
133 let offset = (self.config.offset / BITS_PER_BYTE) as usize;
134 data[offset + 4]
135 }
136 fn read_crc(&self, data: &[u8]) -> u16 {
137 let offset = (self.config.offset / BITS_PER_BYTE) as usize;
138 u16::from_be_bytes([data[offset], data[offset + 1]])
139 }
140
141 fn do_checks(&mut self, check_items: Profile6Check) -> E2EStatus {
142 if check_items.calculated_crc != check_items.rx_crc {
143 return E2EStatus::CrcError;
144 }
145 if check_items.rx_data_length != check_items.data_len {
146 return E2EStatus::DataLengthError;
147 }
148 let status = self.validate_counter(check_items.rx_counter);
149 self.counter = check_items.rx_counter;
150 status
151 }
152 fn check_counter_delta(&self, received_counter: u8) -> u8 {
154 if received_counter >= self.counter {
155 received_counter - self.counter
156 } else {
157 ((COUNTER_MODULO + received_counter as u16 - self.counter as u16) % COUNTER_MODULO)
159 as u8
160 }
161 }
162 fn validate_counter(&self, rx_counter: u8) -> E2EStatus {
163 let delta = self.check_counter_delta(rx_counter);
164
165 if delta == 0 {
166 if self.initialized {
167 E2EStatus::Repeated
168 } else {
169 E2EStatus::Ok
170 }
171 } else if delta == 1 {
172 E2EStatus::Ok
173 } else if delta >= 2 && delta <= self.config.max_delta_counter {
174 E2EStatus::OkSomeLost
175 } else {
176 E2EStatus::WrongSequence
177 }
178 }
179}
180
181impl E2EProfile for Profile6 {
182 type Config = Profile6Config;
183
184 fn new(config: Self::Config) -> E2EResult<Self> {
185 Self::validate_config(&config)?;
187 Ok(Self {
188 config,
189 counter: 0,
190 initialized: false,
191 })
192 }
193
194 fn protect(&mut self, data: &mut [u8]) -> E2EResult<()> {
195 self.validate_length(data.len() as u16)?;
196 self.write_data_length(data);
197 self.write_counter(data);
198 let calculated_crc = self.compute_crc(data);
199 self.write_crc(calculated_crc, data);
200 self.increment_counter();
201 Ok(())
202 }
203
204 fn check(&mut self, data: &[u8]) -> E2EResult<E2EStatus> {
205 self.validate_length(data.len() as u16)?;
207 let check_items = Profile6Check {
208 rx_data_length: self.read_data_length(data),
209 rx_counter: self.read_counter(data),
210 rx_crc: self.read_crc(data),
211 calculated_crc: self.compute_crc(data),
212 data_len: data.len() as u16,
213 };
214 let status = self.do_checks(check_items);
215 if !self.initialized && matches!(status, E2EStatus::Ok | E2EStatus::OkSomeLost) {
216 self.initialized = true;
217 }
218 Ok(status)
219 }
220}
221
222#[cfg(test)]
223mod tests {
224 use super::*;
225 #[test]
226 fn test_profile6_basic_example() {
227 let mut profile_tx = Profile6::new(Profile6Config::default()).unwrap();
228 let mut profile_rx = Profile6::new(Profile6Config::default()).unwrap();
229
230 let mut data = vec![0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00];
231 profile_tx.protect(&mut data).unwrap();
232 assert_eq!(data[0], 0xb1);
234 assert_eq!(data[1], 0x55);
235 assert_eq!(data[2], 0x00);
237 assert_eq!(data[3], 0x08);
238 assert_eq!(data[4], 0x00);
240 assert_eq!(profile_rx.check(&data).unwrap(), E2EStatus::Ok);
241 }
242
243 #[test]
244 fn test_profile6_offset_example() {
245 let config = Profile6Config {
246 offset: 64,
247 ..Default::default()
248 };
249
250 let mut profile_tx = Profile6::new(config.clone()).unwrap();
251 let mut profile_rx = Profile6::new(config).unwrap();
252
253 let mut data = vec![
254 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
255 0x00, 0x00,
256 ];
257 profile_tx.protect(&mut data).unwrap();
258 assert_eq!(data[8], 0x4e);
260 assert_eq!(data[9], 0xb7);
261 assert_eq!(data[10], 0x00);
263 assert_eq!(data[11], 0x10);
264 assert_eq!(data[12], 0x00);
266 assert_eq!(profile_rx.check(&data).unwrap(), E2EStatus::Ok);
267 }
268 #[test]
269 fn test_profile6_counter_wraparound() {
270 let mut profile_tx = Profile6::new(Profile6Config::default()).unwrap();
271 let mut profile_rx = Profile6::new(Profile6Config::default()).unwrap();
272
273 let mut data = vec![0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00];
274 profile_tx.protect(&mut data).unwrap();
275 assert_eq!(data[4], 0x00);
277 assert_eq!(profile_rx.check(&data).unwrap(), E2EStatus::Ok);
278 profile_tx.protect(&mut data).unwrap();
279 assert_eq!(data[4], 0x01);
281 assert_eq!(profile_rx.check(&data).unwrap(), E2EStatus::Ok);
282 profile_rx.counter = 0xFE;
283 profile_tx.counter = 0xFF;
284 profile_tx.protect(&mut data).unwrap();
285 assert_eq!(data[4], 0xFF);
287 assert_eq!(profile_rx.check(&data).unwrap(), E2EStatus::Ok);
288 profile_tx.protect(&mut data).unwrap();
289 assert_eq!(data[4], 0x00);
291 assert_eq!(profile_rx.check(&data).unwrap(), E2EStatus::Ok);
292 profile_tx.protect(&mut data).unwrap();
293 }
294}