mod context;
mod service;
use crate::{constants::LOG_TAG_CLIENT, error::DoCanError, SecurityAlgo};
use iso14229_1::{
request::Request,
response::{Code, Response},
Configuration, DataIdentifier, Service, TesterPresentType, SUPPRESS_POSITIVE,
};
use iso15765_2::{
can::{Address, AddressType, CanIsoTp},
IsoTp, IsoTpError,
};
use rs_can::{CanDevice, CanFrame};
use rsutil::types::ByteOrder;
use std::{fmt::Display, hash::Hash};
#[derive(Clone)]
pub struct DoCanClient<D, C, F>
where
D: CanDevice<Channel = C, Frame = F> + Clone + 'static,
C: Display + Clone + Hash + Eq + 'static,
F: CanFrame<Channel = C> + Clone + Display + 'static,
{
isotp: CanIsoTp<D, C, F>,
context: context::Context,
}
impl<D, C, F> DoCanClient<D, C, F>
where
D: CanDevice<Channel = C, Frame = F> + Clone + Send + 'static,
C: Display + Clone + Hash + Eq + Send + Sync + 'static,
F: CanFrame<Channel = C> + Clone + Display + 'static,
{
pub async fn new(
device: D,
channel: C,
addr: Address,
byte_order: ByteOrder,
p2_offset: Option<u16>,
) -> Self {
Self {
isotp: CanIsoTp::new(device, channel, addr, false).await,
context: context::Context::new(byte_order, p2_offset),
}
}
#[inline(always)]
pub fn tp_layer(&mut self) -> &mut CanIsoTp<D, C, F> {
&mut self.isotp
}
#[inline(always)]
pub fn byte_order(&self) -> ByteOrder {
self.context.byte_order
}
#[inline(always)]
pub async fn update_address(&self, address: Address) {
self.isotp.update_address(address).await;
}
#[inline(always)]
pub async fn update_security_algo(&self, algo: SecurityAlgo) {
self.context.set_security_algo(algo).await;
}
#[inline(always)]
pub async fn add_data_identifier(&self, did: DataIdentifier, length: usize) {
self.context.add_did(did, length).await;
}
#[inline(always)]
pub async fn remove_data_identifier(&self, did: DataIdentifier) {
self.context.remove_did(&did).await;
}
#[inline(always)]
fn make_request<T: Into<Vec<u8>>>(
service: Service,
sub_func: Option<u8>,
data: T,
cfg: &Configuration,
) -> Result<Request, DoCanError> {
Request::new::<Vec<_>>(service, sub_func, data.into(), cfg)
.map_err(DoCanError::Iso14229Error)
}
#[inline(always)]
async fn send_and_parse<T>(
&self,
addr_type: AddressType,
request: Request,
sub_check: Option<(u8, Service)>,
cfg: &Configuration,
) -> Result<T, DoCanError>
where
T: iso14229_1::ResponseData,
{
let response = self
.send_and_response(addr_type, request, sub_check, cfg)
.await?;
response.data::<T>(cfg).map_err(DoCanError::Iso14229Error)
}
fn response_service_check(response: &Response, target: Service) -> Result<bool, DoCanError> {
let service = response.service();
if response.is_negative() {
let nrc_code = response.nrc_code().map_err(DoCanError::Iso14229Error)?;
match nrc_code {
Code::RequestCorrectlyReceivedResponsePending => Ok(true),
_ => Err(DoCanError::NRCError {
service,
code: nrc_code,
}),
}
} else if service != target {
Err(DoCanError::UnexpectedResponse {
expect: target,
actual: service,
})
} else {
Ok(false)
}
}
#[inline(always)]
async fn suppress_positive_sr(
&self,
addr_type: AddressType,
request: Request,
suppress_positive: bool,
sub_check: Option<(u8, Service)>,
cfg: &Configuration,
) -> Result<Option<Response>, DoCanError> {
match self.send_and_response(addr_type, request, None, cfg).await {
Ok(r) => {
if let Some((source, service)) = sub_check {
Self::sub_func_check(&r, source, service)?;
}
Ok(Some(r))
}
Err(e) => match e {
DoCanError::IsoTpError(e) => match e {
IsoTpError::Timeout { .. } => {
if suppress_positive {
Ok(None)
} else {
Err(DoCanError::IsoTpError(e))
}
}
_ => Err(DoCanError::IsoTpError(e)),
},
_ => Err(e),
},
}
}
async fn send_and_response(
&self,
addr_type: AddressType,
request: Request,
sub_check: Option<(u8, Service)>,
cfg: &Configuration,
) -> Result<Response, DoCanError> {
let service = request.service();
let data: Vec<_> = request.into();
let timing = self.context.get_session_timing().await;
let p2_offset = self.context.p2_offset;
let _ = &self
.isotp
.transmit(addr_type, data)
.await
.map_err(DoCanError::IsoTpError)?;
let data = &self
.isotp
.wait_data(timing.p2_ms() + p2_offset)
.await
.map_err(DoCanError::IsoTpError)?;
let mut response = Response::try_from((data, cfg)).map_err(DoCanError::Iso14229Error)?;
while Self::response_service_check(&response, service)? {
rsutil::debug!(
"{} tester present when {:?}",
LOG_TAG_CLIENT,
Code::RequestCorrectlyReceivedResponsePending
);
let (_, request) =
Self::tester_present_request(TesterPresentType::Zero, true, cfg).await?;
let data: Vec<_> = request.into();
let _ = &self
.isotp
.transmit(addr_type, data)
.await
.map_err(DoCanError::IsoTpError)?;
let data = &self
.isotp
.wait_data(timing.p2_star_ms())
.await
.map_err(DoCanError::IsoTpError)?;
response = Response::try_from((data, cfg)).map_err(DoCanError::Iso14229Error)?;
}
if let Some((source, service)) = sub_check {
Self::sub_func_check(&response, source, service)?;
}
Ok(response)
}
fn sub_func_check(response: &Response, source: u8, service: Service) -> Result<(), DoCanError> {
match response.sub_function() {
Some(v) => {
let target = v.origin();
if target != source {
Err(DoCanError::UnexpectedSubFunction {
service,
expect: source,
actual: target,
})
} else {
Ok(())
}
}
None => Err(DoCanError::OtherError(format!(
"response of service `{}` got an empty sub-function",
service
))),
}
}
#[inline(always)]
async fn tester_present_request(
test_type: TesterPresentType,
suppress_positive: bool,
cfg: &Configuration,
) -> Result<(Service, Request), DoCanError> {
let service = Service::TesterPresent;
let mut sub_func = test_type.into();
if suppress_positive {
sub_func |= SUPPRESS_POSITIVE;
}
let request = Self::make_request(service, Some(sub_func), vec![], cfg)?;
Ok((service, request))
}
}