1use crate::{E2EError, E2EProfile, E2EResult, E2EStatus};
13use crc::{Crc, CRC_64_XZ};
14
15const BITS_PER_BYTE: u32 = 8;
17const COUNTER_MAX: u32 = 0xFFFFFFFF;
18const COUNTER_MODULO: u64 = 0x100000000;
19
20#[derive(Debug, Clone)]
22pub struct Profile7Config {
23 pub data_id: u32,
25 pub offset: u32,
27 pub min_data_length: u32,
29 pub max_data_length: u32,
31 pub max_delta_counter: u32,
33}
34
35#[derive(Debug, Clone)]
37pub struct Profile7Check {
38 rx_data_length: u32,
39 rx_counter: u32,
40 rx_data_id: u32,
41 rx_crc: u64,
42 calculated_crc: u64,
43 data_len: u32,
44}
45
46impl Default for Profile7Config {
47 fn default() -> Self {
48 Self {
49 data_id: 0x0a0b0c0d,
50 offset: 0x00000000,
51 min_data_length: 160, max_data_length: 32768, max_delta_counter: 1,
54 }
55 }
56}
57
58#[derive(Clone)]
62pub struct Profile7 {
63 config: Profile7Config,
64 counter: u32,
65 initialized: bool,
66}
67
68impl Profile7 {
69 fn validate_config(config: &Profile7Config) -> E2EResult<()> {
71 if config.min_data_length < 20 * BITS_PER_BYTE {
72 return Err(E2EError::InvalidConfiguration(
73 "Minimum Data length shall be larger than 20B".into(),
74 ));
75 }
76 if config.max_data_length < config.min_data_length {
77 return Err(E2EError::InvalidConfiguration(
78 "Maximum Data length shall be larger than MinDataLength".into(),
79 ));
80 }
81 if config.max_delta_counter == 0 || config.max_delta_counter == COUNTER_MAX {
82 return Err(E2EError::InvalidConfiguration(format!(
83 "Max delta counter must be between 1 and {}",
84 COUNTER_MAX
85 )));
86 }
87 Ok(())
88 }
89 fn validate_length(&self, len: u32) -> E2EResult<()> {
91 let min_bytes = self.config.min_data_length / BITS_PER_BYTE;
92 let max_bytes = self.config.max_data_length / BITS_PER_BYTE;
93 if len < min_bytes || max_bytes < len {
94 return Err(E2EError::InvalidDataFormat(format!(
95 "Expected {} - {} bytes, got {} bytes",
96 min_bytes, max_bytes, len
97 )));
98 }
99 Ok(())
100 }
101 fn write_data_length(&self, data: &mut [u8]) {
102 let offset = (self.config.offset / BITS_PER_BYTE) as usize;
103 let len32 = data.len() as u32;
104 data[offset + 8..=offset + 11].copy_from_slice(&len32.to_be_bytes());
105 }
106 fn write_counter(&self, data: &mut [u8]) {
107 let offset = (self.config.offset / BITS_PER_BYTE) as usize;
108 data[offset + 12..=offset + 15].copy_from_slice(&self.counter.to_be_bytes());
109 }
110 fn write_data_id(&self, data: &mut [u8]) {
111 let offset = (self.config.offset / BITS_PER_BYTE) as usize;
112 data[offset + 16..=offset + 19].copy_from_slice(&self.config.data_id.to_be_bytes());
113 }
114 fn compute_crc(&self, data: &[u8]) -> u64 {
115 let crc: Crc<u64> = Crc::<u64>::new(&CRC_64_XZ);
116 let mut digest = crc.digest();
117 let offset = (self.config.offset / BITS_PER_BYTE) as usize;
118 digest.update(&data[0..offset]); digest.update(&data[(offset + 8)..]); digest.finalize()
121 }
122 fn write_crc(&self, calculated_crc: u64, data: &mut [u8]) {
123 let offset = (self.config.offset / BITS_PER_BYTE) as usize;
124 data[offset..=offset + 7].copy_from_slice(&calculated_crc.to_be_bytes());
125 }
126 fn increment_counter(&mut self) {
127 self.counter = if self.counter == COUNTER_MAX {
128 0x00000000
129 } else {
130 (self.counter + 1) & COUNTER_MAX
131 };
132 }
133
134 fn read_data_length(&self, data: &[u8]) -> u32 {
135 let offset = (self.config.offset / BITS_PER_BYTE) as usize;
136 u32::from_be_bytes([
137 data[offset + 8],
138 data[offset + 9],
139 data[offset + 10],
140 data[offset + 11],
141 ])
142 }
143 fn read_counter(&self, data: &[u8]) -> u32 {
144 let offset = (self.config.offset / BITS_PER_BYTE) as usize;
145 u32::from_be_bytes([
146 data[offset + 12],
147 data[offset + 13],
148 data[offset + 14],
149 data[offset + 15],
150 ])
151 }
152 fn read_data_id(&self, data: &[u8]) -> u32 {
153 let offset = (self.config.offset / BITS_PER_BYTE) as usize;
154 u32::from_be_bytes([
155 data[offset + 16],
156 data[offset + 17],
157 data[offset + 18],
158 data[offset + 19],
159 ])
160 }
161 fn read_crc(&self, data: &[u8]) -> u64 {
162 let offset = (self.config.offset / BITS_PER_BYTE) as usize;
163 u64::from_be_bytes([
164 data[offset],
165 data[offset + 1],
166 data[offset + 2],
167 data[offset + 3],
168 data[offset + 4],
169 data[offset + 5],
170 data[offset + 6],
171 data[offset + 7],
172 ])
173 }
174
175 fn do_checks(&mut self, check_items: Profile7Check) -> E2EStatus {
176 if check_items.calculated_crc != check_items.rx_crc {
177 return E2EStatus::CrcError;
178 }
179 if check_items.rx_data_id != self.config.data_id {
180 return E2EStatus::DataIdError;
181 }
182 if check_items.rx_data_length != check_items.data_len {
183 return E2EStatus::DataLengthError;
184 }
185 let status = self.validate_counter(check_items.rx_counter);
186 self.counter = check_items.rx_counter;
187 status
188 }
189 fn check_counter_delta(&self, received_counter: u32) -> u32 {
191 if received_counter >= self.counter {
192 received_counter - self.counter
193 } else {
194 ((COUNTER_MODULO + received_counter as u64 - self.counter as u64) % COUNTER_MODULO)
196 as u32
197 }
198 }
199 fn validate_counter(&self, rx_counter: u32) -> E2EStatus {
200 let delta = self.check_counter_delta(rx_counter);
201
202 if delta == 0 {
203 if self.initialized {
204 E2EStatus::Repeated
205 } else {
206 E2EStatus::Ok
207 }
208 } else if delta == 1 {
209 E2EStatus::Ok
210 } else if delta >= 2 && delta <= self.config.max_delta_counter {
211 E2EStatus::OkSomeLost
212 } else {
213 E2EStatus::WrongSequence
214 }
215 }
216}
217
218impl E2EProfile for Profile7 {
219 type Config = Profile7Config;
220
221 fn new(config: Self::Config) -> E2EResult<Self> {
222 Self::validate_config(&config)?;
224 Ok(Self {
225 config,
226 counter: 0,
227 initialized: false,
228 })
229 }
230
231 fn protect(&mut self, data: &mut [u8]) -> E2EResult<()> {
232 self.validate_length(data.len() as u32)?;
233 self.write_data_length(data);
234 self.write_counter(data);
235 self.write_data_id(data);
236 let calculated_crc = self.compute_crc(data);
237 self.write_crc(calculated_crc, data);
238 self.increment_counter();
239 Ok(())
240 }
241
242 fn check(&mut self, data: &[u8]) -> E2EResult<E2EStatus> {
243 self.validate_length(data.len() as u32)?;
245 let check_items = Profile7Check {
246 rx_data_length: self.read_data_length(data),
247 rx_counter: self.read_counter(data),
248 rx_crc: self.read_crc(data),
249 rx_data_id: self.read_data_id(data),
250 calculated_crc: self.compute_crc(data),
251 data_len: data.len() as u32,
252 };
253 let status = self.do_checks(check_items);
254 if !self.initialized && matches!(status, E2EStatus::Ok | E2EStatus::OkSomeLost) {
255 self.initialized = true;
256 }
257 Ok(status)
258 }
259}
260
261#[cfg(test)]
262mod tests {
263 use super::*;
264 #[test]
265 fn test_profile7_basic_example() {
266 let mut profile_tx = Profile7::new(Profile7Config::default()).unwrap();
267 let mut profile_rx = Profile7::new(Profile7Config::default()).unwrap();
268
269 let mut data = vec![
270 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
271 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
272 ];
273 profile_tx.protect(&mut data).unwrap();
274 assert_eq!(data[0], 0x1f);
276 assert_eq!(data[1], 0xb2);
277 assert_eq!(data[2], 0xe7);
278 assert_eq!(data[3], 0x37);
279 assert_eq!(data[4], 0xfc);
280 assert_eq!(data[5], 0xed);
281 assert_eq!(data[6], 0xbc);
282 assert_eq!(data[7], 0xd9);
283 assert_eq!(data[8], 0x00);
285 assert_eq!(data[9], 0x00);
286 assert_eq!(data[10], 0x00);
287 assert_eq!(data[11], 0x18);
288 assert_eq!(data[12], 0x00);
290 assert_eq!(data[13], 0x00);
291 assert_eq!(data[14], 0x00);
292 assert_eq!(data[15], 0x00);
293 assert_eq!(data[16], 0x0a);
295 assert_eq!(data[17], 0x0b);
296 assert_eq!(data[18], 0x0c);
297 assert_eq!(data[19], 0x0d);
298 assert_eq!(profile_rx.check(&data).unwrap(), E2EStatus::Ok);
299 }
300
301 #[test]
302 fn test_profile7_offset_example() {
303 let config = Profile7Config {
304 offset: 64,
305 ..Default::default()
306 };
307
308 let mut profile_tx = Profile7::new(config.clone()).unwrap();
309 let mut profile_rx = Profile7::new(config).unwrap();
310
311 let mut data = vec![
312 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
313 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
314 0x00, 0x00, 0x00, 0x00,
315 ];
316 profile_tx.protect(&mut data).unwrap();
317 assert_eq!(data[8], 0x17);
319 assert_eq!(data[9], 0xf7);
320 assert_eq!(data[10], 0xc8);
321 assert_eq!(data[11], 0x17);
322 assert_eq!(data[12], 0x32);
323 assert_eq!(data[13], 0x38);
324 assert_eq!(data[14], 0x65);
325 assert_eq!(data[15], 0xa8);
326 assert_eq!(data[16], 0x00);
328 assert_eq!(data[17], 0x00);
329 assert_eq!(data[18], 0x00);
330 assert_eq!(data[19], 0x20);
331 assert_eq!(data[20], 0x00);
333 assert_eq!(data[21], 0x00);
334 assert_eq!(data[22], 0x00);
335 assert_eq!(data[23], 0x00);
336 assert_eq!(data[24], 0x0a);
338 assert_eq!(data[25], 0x0b);
339 assert_eq!(data[26], 0x0c);
340 assert_eq!(data[27], 0x0d);
341 assert_eq!(profile_rx.check(&data).unwrap(), E2EStatus::Ok);
342 }
343 #[test]
344 fn test_profile7_counter_wraparound() {
345 let config = Profile7Config {
346 offset: 64,
347 ..Default::default()
348 };
349
350 let mut profile_tx = Profile7::new(config.clone()).unwrap();
351 let mut profile_rx = Profile7::new(config).unwrap();
352
353 let mut data = vec![
354 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
355 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
356 0x00, 0x00, 0x00, 0x00,
357 ];
358 profile_tx.protect(&mut data).unwrap();
359 assert_eq!(data[20], 0x00);
361 assert_eq!(data[21], 0x00);
362 assert_eq!(data[22], 0x00);
363 assert_eq!(data[23], 0x00);
364 assert_eq!(profile_rx.check(&data).unwrap(), E2EStatus::Ok);
365 profile_tx.protect(&mut data).unwrap();
366 assert_eq!(data[20], 0x00);
368 assert_eq!(data[21], 0x00);
369 assert_eq!(data[22], 0x00);
370 assert_eq!(data[23], 0x01);
371 assert_eq!(profile_rx.check(&data).unwrap(), E2EStatus::Ok);
372 profile_rx.counter = 0xFFFFFFFE;
373 profile_tx.counter = 0xFFFFFFFF;
374 profile_tx.protect(&mut data).unwrap();
375 assert_eq!(data[20], 0xFF);
377 assert_eq!(data[21], 0xFF);
378 assert_eq!(data[22], 0xFF);
379 assert_eq!(data[23], 0xFF);
380 assert_eq!(profile_rx.check(&data).unwrap(), E2EStatus::Ok);
381 profile_tx.protect(&mut data).unwrap();
382 assert_eq!(data[20], 0x00);
384 assert_eq!(data[21], 0x00);
385 assert_eq!(data[22], 0x00);
386 assert_eq!(data[23], 0x00);
387 assert_eq!(profile_rx.check(&data).unwrap(), E2EStatus::Ok);
388 }
389}