use crate::crc::{crc_compute, params_for, CrcPreset};
use crate::e2e::{CheckInfo, E2eError, E2eProfile};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct P11Config {
pub data_id: u16,
pub crc: CrcPreset, }
pub struct Profile11 { cfg: P11Config }
impl Profile11 {
pub fn preset_sae_j1850(data_id: u16) -> Self {
Self::new(P11Config { data_id, crc: CrcPreset::Crc8SaeJ1850 })
}
pub fn preset_h2f(data_id: u16) -> Self {
Self::new(P11Config { data_id, crc: CrcPreset::Crc8H2F })
}
pub fn new(cfg: P11Config) -> Self { Self { cfg } }
}
impl E2eProfile for Profile11 {
fn protect(&self, payload: &[u8], counter: u32) -> Result<Vec<u8>, E2eError> {
if payload.len() + 4 > 8 { return Err(E2eError::LengthError); }
let mut buf = Vec::with_capacity(payload.len() + 4);
buf.extend_from_slice(payload);
buf.extend_from_slice(&self.cfg.data_id.to_be_bytes());
buf.push((counter & 0xFF) as u8);
let crc = crc_compute(params_for(self.cfg.crc), &buf) & 0xFF;
buf.push(crc as u8);
Ok(buf)
}
fn check(&self, frame: &[u8]) -> Result<CheckInfo, E2eError> {
if frame.len() < 4 { return Err(E2eError::LengthError); }
let len = frame.len();
let crc_recv = frame[len - 1];
let counter = frame[len - 2];
let data_id = u16::from_be_bytes([frame[len - 4], frame[len - 3]]);
if data_id != self.cfg.data_id { return Err(E2eError::DataIdMismatch); }
let want = (crc_compute(params_for(self.cfg.crc), &frame[..len - 1]) & 0xFF) as u8;
if want != crc_recv { return Err(E2eError::CrcMismatch); }
Ok(CheckInfo { counter: counter as u32, ok: true })
}
fn payload_len(&self, frame: &[u8]) -> Option<usize> {
frame.len().checked_sub(4)
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::{E2eSession, CounterPolicy, E2eProfile, E2eError};
#[test]
fn p11_roundtrip() {
let mut tx = E2eSession::new(
Profile11::preset_sae_j1850(0x0042),
CounterPolicy::MonotonicRollover { bits: 8, step: 1 },
);
let frame = tx.wrap(&[1,2,3,4]).unwrap();
let mut rx = E2eSession::new(
Profile11::preset_sae_j1850(0x0042),
CounterPolicy::MonotonicRollover { bits: 8, step: 1 },
);
let pl = rx.unwrap(&frame).unwrap();
assert_eq!(pl, &[1,2,3,4]);
}
#[test]
fn p11_detect_crc_and_dataid() {
let p = Profile11::preset_h2f(0xABCD);
let mut f = p.protect(&[7,7,7], 10).unwrap();
f[0] ^= 0x01;
assert!(matches!(p.check(&f), Err(E2eError::CrcMismatch)));
let mut f2 = p.protect(&[7,7,7], 10).unwrap();
let len = f2.len();
f2[len - 3] ^= 0xFF; assert!(matches!(p.check(&f2), Err(E2eError::DataIdMismatch)));
}
#[test]
fn p11_counter_policy() {
use crate::E2eProfile;
let prof = Profile11::preset_sae_j1850(0x2222);
let f1 = prof.protect(&[], 1).unwrap();
let f3 = prof.protect(&[], 3).unwrap(); let f0 = prof.protect(&[], 0).unwrap();
let mut rx = E2eSession::new(
prof,
CounterPolicy::MonotonicRollover { bits: 8, step: 1 },
);
rx.unwrap(&f0).unwrap();
rx.unwrap(&f1).unwrap();
assert!(matches!(rx.unwrap(&f3), Err(E2eError::CounterError)));
}
}