uds_rs/
uds.rs

1#![deny(clippy::all)]
2#![allow(dead_code)]
3//!
4//! # Uds.rs
5//! provides asynchronous UDS communication via socketcan
6//!
7//! For the correct behaviour, you need to have Linux kernel with applied patch:
8//! https://lore.kernel.org/linux-can/20230818114345.142983-1-lukas.magel@posteo.net/#r
9//!
10//!
11//! ## Hierarchy
12//!
13//! module __uds__ - top module containing UdsClient trough which all interaction is provided for the user
14//! services used by UdsClient are stored in separate modules - see for example read_data_by_identifier.rs,
15//! where structure of service module is described
16//!
17//! module __communication__ - basic communication framework. Purpose of this module is to provide send
18//! and receive functionality for UdsClient.
19//!
20//! All communication was designed to be used primarily with ISO 14229-1:2013 definition of UDS.
21//!
22//! # Example:
23//!
24//! For correct behaviour the can interface needs to be setup correctly using following command:
25//! ```bash
26//! sudo ip l set dev can0 up type can bitrate 500000
27//! ```
28//!
29//! ```rust
30//! use uds_rs::{UdsClient, UdsError};
31//!
32//! #[tokio::main(flavor = "current_thread")]
33//! async fn main() -> Result<(), UdsError> {
34//!     // Create client
35//!     let c = UdsClient::new("can0", 0x774, 0x70A)?;
36//!
37//!     // read ecu VIN
38//!     let read_data_result = c.read_data_by_identifier(&[0xf18a]).await;
39//!     match read_data_result {
40//!         Ok(x) => println!("Read data by identifier received {:#x?}", x),
41//!         Err(e) => eprintln!(
42//!             "Read single data by identifier failed with error: {:#x?}",
43//!             e
44//!         ),
45//!     };
46//!
47//!     // reading dtc
48//!     let read_dtc_information = c.report_dtc_by_status_mask(0xff).await;
49//!     match read_dtc_information {
50//!         Ok(x) => println!("Read dtc by status mask: {:#x?}", x),
51//!         Err(e) => eprintln!("Clear diagnostic information failed with error: {:#x?}", e),
52//!     }
53//!
54//!     // clear all stored dtc
55//!     let clear_dtc_information = c.clear_diagnostic_information(0xffffff).await;
56//!     match clear_dtc_information {
57//!         Ok(x) => println!("{:#x?}", x),
58//!         Err(e) => eprintln!("Clear diagnostic information failed with error: {:#x?}", e),
59//!     };
60//!     Ok(())
61//! }
62//! ```
63//! # Notes for development
64//! ## Communication architecture
65//! Current communication architecture is strictly bounded request-response together. It would be
66//! much better to have these two interactions separated into queues and adding one producer for writes and one consumer
67//! for reads.
68//!
69//! Without this functionality the services like ReadDataByPeriodicIdentifier cannot be implemented.
70//!
71//! ## Services implementation
72//! each service consists of three steps  
73//! __compose function__ - serializing service method arguments and other needed
74//! data to Vec\<u8\>  
75//! __send and receive__ - passing composed vector as slice to the communication backend and returning raw response  
76//! __parse function__ - parsing received raw response &\[u8\] and serializing it into UdsMessage
77//!
78mod communication;
79
80mod clear_diagnostic_information;
81mod ecu_reset;
82mod read_data_by_identifier;
83mod read_dtc_information;
84mod read_memory_by_address;
85mod uds_definitions;
86mod write_data_by_identifier;
87
88pub use crate::uds::clear_diagnostic_information::*;
89pub use crate::uds::communication::*;
90pub use crate::uds::ecu_reset::*;
91pub use crate::uds::read_data_by_identifier::*;
92pub use crate::uds::read_dtc_information::*;
93pub use crate::uds::read_memory_by_address::*;
94pub use crate::uds::uds_definitions::*;
95pub use crate::uds::write_data_by_identifier::*;
96#[allow(unused_imports)]
97use log::{debug, error, info, trace, warn};
98use thiserror::Error;
99
100pub type EcuResponseResult = Result<UdsResponse, UdsError>;
101
102/// All possible services containing responses
103/// DataFormat represents wether the parsing into response struct was succesful
104#[derive(Debug, PartialEq)]
105pub enum UdsResponse {
106    EcuReset(DataFormat<EcuResetResponse>),
107    ReadDataByIdentifier(DataFormat<ReadDataByIdentifierResponse>),
108    ReadMemoryByAddress(DataFormat<ReadMemoryByAddressResponse>),
109    ReadDTCInformation(DataFormat<ReadDTCInformationResponse>),
110    ClearDiagnosticInformation,
111    WriteDataByIdentifier(DataFormat<WriteDataByIdentifierResponse>),
112}
113
114/// If program was able to parse received data, the response struct will be stored in Parsed.
115/// If parsing was not successful, the Raw will contain all received data, without first byte (SID)
116/// which is encoded in UdsResponse Enum
117#[derive(Debug, PartialEq)]
118pub enum DataFormat<T> {
119    Parsed(T),
120    Raw(Vec<u8>),
121}
122
123/// Containing possible errors and negative responses
124#[derive(Error, Debug, PartialEq)]
125pub enum UdsError {
126    #[error(
127        "Response received does not have expected SID. Expected: {expected:x}, Received: {received:x}"
128    )]
129    SidMismatch {
130        expected: u8,
131        received: u8,
132        raw_message: Vec<u8>,
133    },
134    #[error("Sent and received data identifier don't match. Expected: {expected:x}, Received: {received:x}")]
135    DidMismatch {
136        expected: u16,
137        received: u16,
138        raw_message: Vec<u8>,
139    },
140    #[error("Received message doesn't correspond to expected length. Received message: {raw_message:x?}")]
141    InvalidLength { raw_message: Vec<u8> },
142    #[error("Negative response code was received: {nrc:?}")]
143    NRC { nrc: NrcData },
144    #[error("Was not able to represent provided NRC: {unknown_nrc:x} as the valid NRC")]
145    UnknownNRC { rejected_sid: u8, unknown_nrc: u8 },
146    #[error("Received message has length of 0")]
147    ResponseEmpty,
148    #[error("Subfunction {unsupported_subfunction:x} is not supported for used service")]
149    UnsupportedSubfunction { unsupported_subfunction: u8 },
150    #[error("Argument or combination of entered arguments is not valid")]
151    InvalidArgument,
152    #[error("something is not correct with received data the data: {raw_message:x?}")]
153    ResponseIncorrect { raw_message: Vec<u8> },
154    #[error("feature you tried to call is not yet implemented")]
155    NotImplemented,
156    #[error("Request to be sent is empty")]
157    RequestEmpty,
158    #[error("Error from lower layer {error:?}")]
159    CommunicationError { error: UdsCommunicationError },
160}
161
162/// Struct containing rejected sid and nrc for UdsError::Enc type
163#[derive(Debug, PartialEq)]
164pub struct NrcData {
165    rejected_sid: u8,
166    nrc: NegativeResponseCode,
167}
168
169impl From<UdsCommunicationError> for UdsError {
170    fn from(error: UdsCommunicationError) -> UdsError {
171        UdsError::CommunicationError { error }
172    }
173}
174
175impl From<communication::Error> for UdsError {
176    fn from(error: communication::Error) -> UdsError {
177        let error: UdsCommunicationError = error.into();
178        UdsError::CommunicationError { error }
179    }
180}
181
182/// Main struct providing all API calls.
183///
184pub struct UdsClient {
185    socket: UdsSocket,
186}
187
188impl UdsClient {
189    pub fn new(
190        canifc: &str,
191        src: impl Into<Id>,
192        dst: impl Into<Id>,
193    ) -> Result<UdsClient, UdsError> {
194        Ok(UdsClient {
195            socket: UdsSocket::new(canifc, src, dst)?,
196        })
197    }
198
199    pub fn new_from_socket(socket: UdsSocket) -> UdsClient {
200        UdsClient { socket }
201    }
202
203    async fn send_and_receive(&self, request: &[u8]) -> Result<Vec<u8>, UdsError> {
204        let mut retry_counter = 0;
205        if request.len() == 0 {
206            return Err(UdsError::RequestEmpty);
207        }
208        self.socket.send(&request).await?;
209        let mut raw_response = self.socket.receive().await?;
210        while let Err(e) = parse_for_error(&raw_response) {
211            match e {
212                UdsError::NRC { nrc } => {
213                    if nrc.rejected_sid != request[0] {
214                        return Err(UdsError::SidMismatch {
215                            expected: request[0],
216                            received: nrc.rejected_sid,
217                            raw_message: raw_response,
218                        });
219                    }
220                    match nrc.nrc {
221                        NegativeResponseCode::BusyRepeatRequest => {
222                            // Maybe sleep a little?
223                            retry_counter = retry_counter - 1;
224                            if retry_counter == 0 {
225                                warn!("Service failed after multiple repeats");
226                                return Err(UdsError::NRC { nrc });
227                            }
228                            info!("Received NRC BusyRepeatRequest, repeating");
229                            self.socket.send(&request).await?;
230                            raw_response = self.socket.receive().await?;
231                        }
232                        NegativeResponseCode::RequestCorrectlyReceivedResponsePending => {
233                            info!("NRC RequestCorrectlyReceivedResponsePending received, waiting for next response");
234                            raw_response = self.socket.receive().await?;
235                            break;
236                        }
237                        _ => return Err(UdsError::NRC { nrc }),
238                    }
239                }
240                _ => return Err(e),
241            }
242        }
243        Ok(raw_response)
244    }
245}
246
247fn parse_for_error(raw_response: &[u8]) -> Result<(), UdsError> {
248    let mut response_iter = raw_response.iter();
249    let sid = *response_iter.next().ok_or(UdsError::ResponseEmpty)?;
250    if sid != NEGATIVE_RESPONSE_SID {
251        return Ok(());
252    }
253    let rejected_sid = *response_iter.next().ok_or(UdsError::ResponseEmpty)?;
254    let nrc: NegativeResponseCode =
255        NegativeResponseCode::try_from(*response_iter.next().ok_or(UdsError::ResponseEmpty)?)
256            .map_err(|e| UdsError::UnknownNRC {
257                rejected_sid,
258                unknown_nrc: e.number,
259            })?;
260    let response = UdsError::NRC {
261        nrc: NrcData { rejected_sid, nrc },
262    };
263    Err(response)
264}
265
266#[cfg(test)]
267mod tests {
268    use crate::uds::uds_definitions::NEGATIVE_RESPONSE_SID;
269    use crate::uds::{parse_for_error, UdsError};
270
271    #[test]
272    fn test_parse_for_error_wrong_nrc() {
273        let raw_response = vec![NEGATIVE_RESPONSE_SID, 0x11, 0xff];
274        let expected = UdsError::UnknownNRC {
275            rejected_sid: 0x11,
276            unknown_nrc: 0xff,
277        };
278        let result = parse_for_error(&raw_response);
279        assert_eq!(Err(expected), result);
280    }
281}