Skip to main content

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,                  // The CAN socket channel to transmit data
54    id: Id,                      // The identifier used for the CAN message
55    resp: &'a Arc<ResponseSlot>, // A reference to the response slot for handling responses
56}
57
58#[allow(dead_code)]
59impl<'a, T: CanSocketTx> UdsClient<'a, T> {
60    /// Create a new UdsClient instance.
61    ///
62    /// Takes a CAN socket channel `channel`, a 32-bit identifier `id`, and a reference
63    /// to a `ResponseSlot` wrapped in `Arc`. The `Id::Extended` is used to create a unique
64    /// identifier for the CAN frame.
65    pub fn new(channel: T, id: u32, resp: &'a LazyLock<Arc<ResponseSlot>>) -> Self {
66        let id = Id::Extended(ExtendedId::new(id).unwrap());
67        Self { channel, id, resp }
68    }
69
70    /// Send a command without expecting a response.
71    ///
72    /// This function sends a command using ISO 15765-2 format, which includes PCI, CMD,
73    /// and ARGS. The `args` are added to the frame and sent using the `send_raw` method.
74    pub async fn send_command<P: Into<u8>, M: Into<u8>>(
75        &mut self,
76        pci: P,
77        cmd: M,
78        args: &[u8],
79    ) -> Result<(), DiagError> {
80        let mut data = vec![pci.into(), cmd.into()];
81        data.extend_from_slice(args);
82        self.send_raw(&data).await
83    }
84
85    /// Send an UDS frame without expecting a response.
86    ///
87    /// This function sends the given `UdsFrame` to the CAN bus using the `send_raw` method
88    /// after converting the frame into a byte vector.
89    pub async fn send_frame(&mut self, frame: UdsFrame) -> Result<(), DiagError> {
90        self.send_raw(&frame.to_vec()?).await
91    }
92
93    /// Send an UDS frame and wait for a response.
94    ///
95    /// This function sends an `UdsFrame` to the CAN bus and waits for a response. If the
96    /// response is valid (`Response::Ok`), it returns the response frame, otherwise returns
97    /// the error contained in `Response::Error`.
98    pub async fn send_frame_with_response(
99        &mut self,
100        frame: UdsFrame,
101    ) -> Result<UdsFrame, DiagError> {
102        match self.send_raw_with_response(&frame.to_vec()?).await? {
103            Response::Ok(items) => {
104                debug!("got response: {:?}", items);
105                Ok(items)
106            }
107            Response::Error(e) => Err(e),
108        }
109    }
110
111    /// Send a command with the response.
112    ///
113    /// This function is similar to `send_command` but expects a response after sending
114    /// the command. It returns the response frame (`UdsFrame`) if successful, or the
115    /// error if something went wrong.
116    pub async fn send_command_with_response<P: Into<u8>, M: Into<u8>>(
117        &mut self,
118        pci: P,
119        cmd: M,
120        args: &[u8],
121    ) -> Result<UdsFrame, DiagError> {
122        let mut data = vec![pci.into(), cmd.into()];
123        data.extend_from_slice(args);
124        match self.send_raw_with_response(&data).await? {
125            Response::Ok(items) => {
126                debug!("got response: {:?}", items);
127                Ok(items)
128            }
129            Response::Error(e) => Err(e),
130        }
131    }
132
133    /// Internal function: Send raw data to the CAN bus.
134    ///
135    /// This function sends the provided byte array `data` as a CAN frame using the `channel`.
136    /// It creates a new `Frame` using the `id` and the data, and transmits it over the CAN bus.
137    async fn send_raw(&mut self, data: &[u8]) -> Result<(), DiagError> {
138        let frame = T::Frame::new(self.id, data).unwrap();
139        println!("send raw data frame: {:?}", frame.data());
140        self.channel.transmit(&frame).await.unwrap();
141        Ok(())
142    }
143
144    /// Internal function: Send raw data to the CAN bus and wait for a response.
145    ///
146    /// This function sends the byte array `data` as a CAN frame and waits for a response using
147    /// the `ResponseSlot`. It uses `wait_for_response` to receive the response, and returns the
148    /// received `Response`.
149    async fn send_raw_with_response(&mut self, data: &[u8]) -> Result<Response, DiagError> {
150        let frame = T::Frame::new(self.id, data).unwrap();
151        self.channel.transmit(&frame).await.unwrap();
152        let response = self.resp.wait_for_response().await;
153        Ok(response)
154    }
155
156    /// Receive a frame from the UDS server.
157    ///
158    /// This function waits for and receives a response from the UDS server using the `ResponseSlot`.
159    /// It blocks until a response is available and returns the response.
160    pub async fn receive(&mut self) -> Response {
161        self.resp.wait_for_response().await
162    }
163}