1use crate::{E2EError, E2EProfile, E2EResult, E2EStatus};
12use crc::{Crc, CRC_16_IBM_3740};
13
14const COUNTER_MAX: u8 = 0xFF;
16const BITS_PER_BYTE: u16 = 8;
17const COUNTER_MODULO: u16 = 0x100;
18
19#[derive(Debug, Clone)]
21pub struct Profile5Config {
22 pub data_length: u16,
24 pub data_id: u16,
26 pub max_delta_counter: u8,
28 pub offset: u16,
30}
31
32#[derive(Debug, Clone)]
34pub struct Profile5Check {
35 rx_counter: u8,
36 rx_crc: u16,
37 calculated_crc: u16,
38}
39
40impl Default for Profile5Config {
41 fn default() -> Self {
42 Self {
43 data_id: 0x1234,
44 offset: 0x0000,
45 data_length: 24, max_delta_counter: 1,
47 }
48 }
49}
50
51#[derive(Clone)]
55pub struct Profile5 {
56 config: Profile5Config,
57 counter: u8,
58 initialized: bool,
59}
60
61impl Profile5 {
62 fn validate_config(config: &Profile5Config) -> E2EResult<()> {
64 if config.data_length < 3 * BITS_PER_BYTE || 4096 * BITS_PER_BYTE < config.data_length {
65 return Err(E2EError::InvalidConfiguration(
66 "Minimum Data length shall be between 3B and 4096B".into(),
67 ));
68 }
69 if config.data_length - 3 * BITS_PER_BYTE < config.offset {
70 return Err(E2EError::InvalidConfiguration(
71 "Offset shall be between 0 and data length - 3B".into(),
72 ));
73 }
74 if config.max_delta_counter == 0 || config.max_delta_counter == COUNTER_MAX {
75 return Err(E2EError::InvalidConfiguration(format!(
76 "Max delta counter must be between 1 and {}",
77 COUNTER_MAX
78 )));
79 }
80 Ok(())
81 }
82 fn validate_length(&self, len: u16) -> E2EResult<()> {
84 let expected_bytes = self.config.data_length / BITS_PER_BYTE;
85 if len != expected_bytes {
86 return Err(E2EError::InvalidDataFormat(format!(
87 "Expected {} bytes, got {} bytes",
88 expected_bytes, len
89 )));
90 }
91 Ok(())
92 }
93 fn write_counter(&self, data: &mut [u8]) {
94 let offset = (self.config.offset / BITS_PER_BYTE) as usize;
95 data[offset + 2] = self.counter;
96 }
97 fn compute_crc(&self, data: &[u8]) -> u16 {
98 let crc: Crc<u16> = Crc::<u16>::new(&CRC_16_IBM_3740);
99 let mut digest = crc.digest();
100 let offset = (self.config.offset / BITS_PER_BYTE) as usize;
101 digest.update(&data[0..offset]); digest.update(&data[(offset + 2)..]); digest.update(&self.config.data_id.to_le_bytes());
104 digest.finalize()
105 }
106 fn write_crc(&self, calculated_crc: u16, data: &mut [u8]) {
107 let offset = (self.config.offset / BITS_PER_BYTE) as usize;
108 data[offset..=offset + 1].copy_from_slice(&calculated_crc.to_le_bytes());
109 }
110 fn increment_counter(&mut self) {
111 self.counter = (self.counter as u16 + 1) as u8 & COUNTER_MAX;
112 }
113 fn read_counter(&self, data: &[u8]) -> u8 {
114 let offset = (self.config.offset / BITS_PER_BYTE) as usize;
115 data[offset + 2]
116 }
117 fn read_crc(&self, data: &[u8]) -> u16 {
118 let offset = (self.config.offset / BITS_PER_BYTE) as usize;
119 u16::from_le_bytes([data[offset], data[offset + 1]])
120 }
121
122 fn do_checks(&mut self, check_items: Profile5Check) -> E2EStatus {
123 if check_items.calculated_crc != check_items.rx_crc {
124 return E2EStatus::CrcError;
125 }
126 let status = self.validate_counter(check_items.rx_counter);
127 self.counter = check_items.rx_counter;
128 status
129 }
130 fn check_counter_delta(&self, received_counter: u8) -> u8 {
132 if received_counter >= self.counter {
133 received_counter - self.counter
134 } else {
135 ((COUNTER_MODULO + received_counter as u16 - self.counter as u16) % COUNTER_MODULO)
137 as u8
138 }
139 }
140 fn validate_counter(&self, rx_counter: u8) -> E2EStatus {
141 let delta = self.check_counter_delta(rx_counter);
142
143 if delta == 0 {
144 if self.initialized {
145 E2EStatus::Repeated
146 } else {
147 E2EStatus::Ok
148 }
149 } else if delta == 1 {
150 E2EStatus::Ok
151 } else if delta >= 2 && delta <= self.config.max_delta_counter {
152 E2EStatus::OkSomeLost
153 } else {
154 E2EStatus::WrongSequence
155 }
156 }
157}
158
159impl E2EProfile for Profile5 {
160 type Config = Profile5Config;
161
162 fn new(config: Self::Config) -> E2EResult<Self> {
163 Self::validate_config(&config)?;
165 Ok(Self {
166 config,
167 counter: 0,
168 initialized: false,
169 })
170 }
171
172 fn protect(&mut self, data: &mut [u8]) -> E2EResult<()> {
173 self.validate_length(data.len() as u16)?;
174 self.write_counter(data);
175 let calculated_crc = self.compute_crc(data);
176 self.write_crc(calculated_crc, data);
177 self.increment_counter();
178 Ok(())
179 }
180
181 fn check(&mut self, data: &[u8]) -> E2EResult<E2EStatus> {
182 self.validate_length(data.len() as u16)?;
184 let check_items = Profile5Check {
185 rx_counter: self.read_counter(data),
186 rx_crc: self.read_crc(data),
187 calculated_crc: self.compute_crc(data),
188 };
189 let status = self.do_checks(check_items);
190 if !self.initialized && matches!(status, E2EStatus::Ok | E2EStatus::OkSomeLost) {
191 self.initialized = true;
192 }
193 Ok(status)
194 }
195}
196
197#[cfg(test)]
198mod tests {
199 use super::*;
200 #[test]
201 fn test_profile5_basic_example() {
202 let config = Profile5Config {
203 data_length: 8 * BITS_PER_BYTE,
204 ..Default::default()
205 };
206
207 let mut profile_tx = Profile5::new(config.clone()).unwrap();
208 let mut profile_rx = Profile5::new(config).unwrap();
209
210 let mut data = vec![0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00];
211 profile_tx.protect(&mut data).unwrap();
212 assert_eq!(data[0], 0x1c);
214 assert_eq!(data[1], 0xca);
215 assert_eq!(data[2], 0x00);
217 assert_eq!(profile_rx.check(&data).unwrap(), E2EStatus::Ok);
218 }
219 #[test]
220 fn test_profile5_offset_example() {
221 let config = Profile5Config {
222 offset: 8 * BITS_PER_BYTE,
223 data_length: 16 * BITS_PER_BYTE,
224 ..Default::default()
225 };
226
227 let mut profile_tx = Profile5::new(config.clone()).unwrap();
228 let mut profile_rx = Profile5::new(config).unwrap();
229
230 let mut data = vec![
231 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
232 0x00, 0x00,
233 ];
234 profile_tx.protect(&mut data).unwrap();
235 assert_eq!(data[8], 0x28);
237 assert_eq!(data[9], 0x91);
238 assert_eq!(data[10], 0x00);
240 assert_eq!(profile_rx.check(&data).unwrap(), E2EStatus::Ok);
241 }
242 #[test]
243 fn test_profile5_counter_wraparound() {
244 let config = Profile5Config {
245 offset: 8 * BITS_PER_BYTE,
246 data_length: 16 * BITS_PER_BYTE,
247 ..Default::default()
248 };
249
250 let mut profile_tx = Profile5::new(config.clone()).unwrap();
251 let mut profile_rx = Profile5::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], 0x28);
260 assert_eq!(data[9], 0x91);
261 assert_eq!(data[10], 0x00);
263 assert_eq!(profile_rx.check(&data).unwrap(), E2EStatus::Ok);
264 for i in 1u8..=0xFF {
265 profile_tx.protect(&mut data).unwrap();
266 assert_eq!(data[10], i);
268 assert_eq!(profile_rx.check(&data).unwrap(), E2EStatus::Ok);
269 }
270 profile_tx.protect(&mut data).unwrap();
271 assert_eq!(data[10], 0x00);
273 assert_eq!(profile_rx.check(&data).unwrap(), E2EStatus::Ok);
274 }
275}