uds_client/uds_client/
client.rs

1//! # UDS Client
2//!
3//! ## Overview
4//! The `UdsClient` provides an implementation for sending and receiving Unified Diagnostic Services (UDS) messages
5//! over a CAN bus interface. It facilitates communication with ECUs (Electronic Control Units) in an automotive
6//! network using UDS over CAN (ISO 14229).
7//!
8//! ## Features
9//! - Supports **sending UDS requests** and **receiving responses** from an ECU.
10//! - Implements **ISO-TP (ISO 15765-2) framing**, allowing for multi-frame messages.
11//! - Handles **CAN IDs** (standard and extended) to ensure correct communication.
12//! - Uses an **async-friendly design** to integrate with Rust's asynchronous runtime.
13//!
14//! ## Usage Example
15//! ```rust
16//! use embedded_can::{nb::Can, Frame, Id};
17//! use uds_client::{UdsClient, DiagError, ResponseSlot};
18//! use std::sync::Arc;
19//!
20//! async fn example_usage<T: Can>(channel: &mut T, resp_slot: &Arc<ResponseSlot>) -> Result<(), DiagError> {
21//!     let id = Id::Extended(ExtendedId::new(0x7DF).unwrap());
22//!     let mut client = UdsClient::new(channel, id, resp_slot);
23//!
24//!     // Example: Sending a diagnostic request
25//!     client.send_command(0x10, &[0x01, 0x02, 0x03]).await?;
26//!
27//!     // Example: Receiving a response
28//!     if let Ok(response) = client.receive().await {
29//!         println!("Received response: {:?}", response);
30//!     }
31//!
32//!     Ok(())
33//! }
34//! ```
35//!
36//! ## Errors
37//! The `UdsClient` may return the following errors:
38//! - `DiagError::Timeout`: When a response is not received within the expected time.
39//! - `DiagError::InvalidResponse`: When the received response does not match the expected UDS format.
40//! - `DiagError::HardwareError`: When there is an issue with the CAN bus or adapter.
41//!
42//! ## Structs
43//! - [`UdsClient`] - The main client struct for handling UDS communication.
44
45use crate::socket_can::CanSocketTx;
46
47use super::{DiagError, Response, ResponseSlot, frame::UdsFrame};
48use embedded_can::{ExtendedId, Frame, Id};
49use log::debug;
50use std::sync::{Arc, LazyLock};
51
52pub struct UdsClient<'a, T: CanSocketTx> {
53    channel: T,
54    id: Id,
55    resp: &'a Arc<ResponseSlot>,
56}
57
58#[allow(dead_code)]
59impl<'a, T: CanSocketTx> UdsClient<'a, T> {
60    pub fn new(channel: T, id: u32, resp: &'a LazyLock<Arc<ResponseSlot>>) -> Self {
61        let id = Id::Extended(ExtendedId::new(id).unwrap());
62        Self { channel, id, resp }
63    }
64
65    /// Send a command without the response.
66    /// The frame includes <PCI> <CMD> <ARGS> as ISO 15765-2
67    pub async fn send_command<P: Into<u8>, M: Into<u8>>(
68        &mut self,
69        pci: P,
70        cmd: M,
71        args: &[u8],
72    ) -> Result<(), DiagError> {
73        let mut data = vec![pci.into(), cmd.into()];
74        data.extend_from_slice(args);
75        self.send_raw(&data).await
76    }
77
78    /// Send an UDS frame without the response.
79    pub async fn send_frame(&mut self, frame: UdsFrame) -> Result<(), DiagError> {
80        if let Ok(data) = frame.to_vec() {
81            self.send_raw(&data).await
82        } else {
83            Err(DiagError::WrongMessage)
84        }
85    }
86
87    /// Send an UDS frame without the response.
88    pub async fn send_frame_with_response(
89        &mut self,
90        frame: UdsFrame,
91    ) -> Result<UdsFrame, DiagError> {
92        if let Ok(data) = frame.to_vec() {
93            match self.send_raw_with_response(&data).await? {
94                Response::Ok(items) => {
95                    debug!("got response: {:?}", items);
96                    Ok(items)
97                }
98                Response::Error => Err(DiagError::Timeout),
99            }
100        } else {
101            Err(DiagError::WrongMessage)
102        }
103    }
104
105    /// Send a command with the response.
106    /// The frame includes <PCI> <CMD> <ARGS> as ISO 15765-2
107    pub async fn send_command_with_response<P: Into<u8>, M: Into<u8>>(
108        &mut self,
109        pci: P,
110        cmd: M,
111        args: &[u8],
112    ) -> Result<UdsFrame, DiagError> {
113        let mut data = vec![pci.into(), cmd.into()];
114        data.extend_from_slice(args);
115        match self.send_raw_with_response(&data).await? {
116            Response::Ok(items) => {
117                debug!("got response: {:?}", items);
118                Ok(items)
119            }
120            Response::Error => Err(DiagError::Timeout),
121        }
122    }
123
124    /// Internal function: send raw data as bytes array to CAN bus.
125    async fn send_raw(&mut self, data: &[u8]) -> Result<(), DiagError> {
126        let frame = T::Frame::new(self.id, data).unwrap();
127        println!("send raw data frame: {:?}", frame.data());
128        self.channel.transmit(&frame).await.unwrap();
129        Ok(())
130    }
131
132    /// Internal function: send raw data as bytes array to CAN bus and wait for a response.
133    async fn send_raw_with_response(&mut self, data: &[u8]) -> Result<Response, DiagError> {
134        let frame = T::Frame::new(self.id, data).unwrap();
135        self.channel.transmit(&frame).await.unwrap();
136        let response = self.resp.wait_for_response().await;
137        Ok(response)
138    }
139
140    /// Receive the frame from UDS server
141    pub async fn receive(&mut self) -> Response {
142        self.resp.wait_for_response().await
143    }
144
145    // pub fn send_raw_frame_with_response(&mut self, frame: T::Frame) -> Result<(), DiagError> {
146    //     if let Err(_) = self.channel.transmit(&frame) {
147    //         return Err(DiagError::NotSupported);
148    //     }
149    //     Ok(())
150    // }
151
152    // pub fn wait_response() -> Result<(), DiagError> {
153    //     Ok(())
154    // }
155}