iso14229-1 0.1.0

A ISO 14229-1 protocol.
Documentation
//! response of Service 10

use crate::{
    constant::{P2_MAX, P2_STAR_MAX},
    error::Error,
    response::{Code, Response, SubFunction},
    utils, Configuration, ResponseData, Service, SessionType,
};
use serde::{ser::SerializeStruct, Deserialize, Deserializer, Serialize, Serializer};
use std::{collections::HashSet, sync::LazyLock};

pub static SESSION_CTRL_NEGATIVES: LazyLock<HashSet<Code>> = LazyLock::new(|| {
    HashSet::from([
        Code::SubFunctionNotSupported,
        Code::IncorrectMessageLengthOrInvalidFormat,
        Code::ConditionsNotCorrect,
    ])
});

#[derive(Debug, Copy, Clone, Eq, PartialEq)]
pub struct SessionTiming {
    pub p2: u16,
    pub p2_star: u16,
}

impl Serialize for SessionTiming {
    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
    where
        S: Serializer,
    {
        let mut state = serializer.serialize_struct("SessionTiming", 2)?;
        state.serialize_field("p2", &self.p2_ms())?;
        state.serialize_field("p2_star", &self.p2_star_ms())?;
        state.end()
    }
}

impl<'de> Deserialize<'de> for SessionTiming {
    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
    where
        D: Deserializer<'de>,
    {
        #[derive(Deserialize)]
        struct SessionTimingVisitor {
            p2: u16,
            p2_star: u32,
        }

        let visitor: SessionTimingVisitor = Deserialize::deserialize(deserializer)?;
        Ok(Self::new(visitor.p2, visitor.p2_star))
    }
}

impl Default for SessionTiming {
    fn default() -> Self {
        Self {
            p2: P2_MAX,
            p2_star: P2_STAR_MAX,
        }
    }
}

impl SessionTiming {
    #[inline]
    pub fn new(p2_ms: u16, p2_star_ms: u32) -> Self {
        let p2 = if p2_ms > P2_MAX { P2_MAX } else { p2_ms };
        let p2_star = (p2_star_ms / 10) as u16;
        let p2_star = if p2_star > P2_STAR_MAX {
            P2_STAR_MAX
        } else {
            p2_star
        };
        Self { p2, p2_star }
    }

    #[inline(always)]
    pub fn p2_ms(&self) -> u64 {
        self.p2 as u64
    }

    #[inline(always)]
    pub fn p2_star_ms(&self) -> u64 {
        self.p2_star as u64 * 10
    }
}

impl<'a> TryFrom<&'a [u8]> for SessionTiming {
    type Error = Error;
    #[allow(unused_mut)]
    fn try_from(data: &'a [u8]) -> Result<Self, Self::Error> {
        let data_len = data.len();
        utils::data_length_check(data_len, 4, true)?;

        let mut offset = 0;

        let mut p2 = u16::from_be_bytes([data[offset], data[offset + 1]]);
        offset += 2;
        let mut p2_star = u16::from_be_bytes([data[offset], data[offset + 1]]);

        #[cfg(not(feature = "session_data_check"))]
        if p2 > P2_MAX || p2_star > P2_STAR_MAX {
            rsutil::warn!("UDS - invalid session data P2: {}, P2*: {}", p2, p2_star);
            if p2 > P2_MAX {
                p2 = P2_MAX;
            }
            if p2_star > P2_STAR_MAX {
                p2_star = P2_STAR_MAX;
            }
        }
        #[cfg(feature = "session_data_check")]
        if p2 > P2_MAX || p2_star > P2_STAR_MAX {
            return Err(Error::InvalidSessionData(format!(
                "P2: {}, P2*: {}",
                p2, p2_star
            )));
        }

        Ok(Self { p2, p2_star })
    }
}

impl From<SessionTiming> for Vec<u8> {
    #[inline]
    fn from(val: SessionTiming) -> Self {
        let mut result = val.p2.to_be_bytes().to_vec();
        result.extend(val.p2_star.to_be_bytes());
        result
    }
}

#[derive(Debug, Default, Clone, Eq, PartialEq)]
pub struct SessionCtrl(pub SessionTiming);

impl From<SessionCtrl> for Vec<u8> {
    fn from(v: SessionCtrl) -> Self {
        v.0.into()
    }
}

impl ResponseData for SessionCtrl {
    fn new_response<T: AsRef<[u8]>>(
        data: T,
        sub_func: Option<u8>,
        _: &Configuration,
    ) -> Result<Response, Error> {
        let data = data.as_ref();
        match sub_func {
            Some(sub_func) => {
                let _ = SessionType::try_from(sub_func)?;

                utils::data_length_check(data.len(), 4, true)?;

                Ok(Response {
                    service: Service::SessionCtrl,
                    negative: false,
                    sub_func: Some(SubFunction::new(sub_func)),
                    data: data.to_vec(),
                })
            }
            None => Err(Error::SubFunctionError(Service::SessionCtrl)),
        }
    }
}

impl TryFrom<(&Response, &Configuration)> for SessionCtrl {
    type Error = Error;
    fn try_from((resp, _): (&Response, &Configuration)) -> Result<Self, Self::Error> {
        let service = resp.service();
        if service != Service::SessionCtrl || resp.sub_func.is_none() {
            return Err(Error::ServiceError(service));
        }

        let timing = SessionTiming::try_from(resp.data.as_slice())?;

        Ok(Self(timing))
    }
}