uds-rs 0.3.0

A asynchronous library implementing UDS protocol over CAN used for automotive diagnostic, standardized by ISO 14229-1:2013.
Documentation
# Unified Diagnostics Services (UDS) client over CAN bus

[![Maintenance: actively-developed](https://img.shields.io/badge/maintenance-actively--developed-brightgreen.svg)](https://github.com/rust-lang/cargo/issues/4121)
[![VERSION](https://img.shields.io/crates/v/uds-rs.svg)](https://crates.io/crates/uds-rs)
[![Docs.rs](https://docs.rs/uds-rs/badge.svg)](https://docs.rs/uds-rs/)
[![CI Status](https://github.com/japawBlob/uds-rs/workflows/CI/badge.svg)](https://github.com/japawBlob/uds-rs/actions)
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)


<!-- The README below is obtained from lib.rs; please update this with: cargo-rdme -->
<!-- DO NOT EDIT TEXT BELOW THIS LINE -->
<!-- cargo-rdme start -->

provides asynchronous UDS communication via socketcan.

All communication was designed to be used primarily with ISO 14229-1:2013 definition of UDS.

## Example

For correct behaviour make sure to set-up a CAN interface first.
You an you a virtual CAN interface for testing purposes, for example with the following command.
To make setting-up a CAN interface easier install [can-utils-rs](https://crates.io/crates/can-utils-rs) package
```bash
cargo install can-utils-rs
```
Then use the following command:
```bash
can-utils-rs
```
Then select the interface type you want to use. Then make sure to match the name of the interface (can0 in the example)
with the one you have set-up.

```rust
// To run the example make sure to set-up a CAN interface first!

use embedded_can::StandardId;
use log::{error, info};
use uds_rs::{ResetType, UdsClient, UdsError, UdsSocketOptions};

#[tokio::main(flavor = "current_thread")]
async fn main() -> Result<(), UdsError> {
    env_logger::init();
    // Create client wtih default options
    let c = UdsClient::new(
        "can0",
        StandardId::new(0x774).expect("Invalid src id"),
        StandardId::new(0x70A).expect("Invalid dst id"),
        UdsSocketOptions::default(),
    )?;

    // read data by identifier (ecu VIN)
    let read_data_result = c.read_data_by_identifier(&[0xf18a]).await;
    match read_data_result {
        Ok(x) => info!("Read data by identifier received {:#x?}", x),
        Err(e) => error!(
            "Read single data by identifier failed with error: {:#x?}",
            e
        ),
    };

    // reading dtc
    let read_dtc_information = c.report_dtc_by_status_mask(0xff).await;
    match read_dtc_information {
        Ok(x) => info!("Read dtc by status mask: {:#x?}", x),
        Err(e) => error!("Read dtc by status mask failed with error: {:#x?}", e),
    }

    // clear all stored dtc
    let clear_dtc_information = c.clear_diagnostic_information(0xffffff).await;
    match clear_dtc_information {
        Ok(x) => info!("{:#x?}", x),
        Err(e) => error!("Clear diagnostic information failed with error: {:#x?}", e),
    };

    // ecu reset
    let ecu_reset_result = c.ecu_reset(ResetType::KeyOffOnReset).await;
    match ecu_reset_result {
        Ok(x) => info!("{:#x?}", x),
        Err(e) => error!("Ecu reset failed with error: {:#x?}", e),
    };

    Ok(())
}
```

#### Example with specific ISO-TP configuration

```rust
// To run the example make sure to set-up a CAN interface first!

use embedded_can::StandardId;
use log::{error, info};
use uds_rs::{ResetType, UdsClient, UdsError, UdsSocketOptions};


#[tokio::main(flavor = "current_thread")]
async fn main() -> Result<(), UdsError> {
    env_logger::init();
    // Create client with VW specific options
    let socket_options = UdsSocketOptions::vw()?;
    let c = UdsClient::new(
        "can0",
        StandardId::new(0x774).expect("Invalid src id"),
        StandardId::new(0x70A).expect("Invalid dst id"),
        socket_options,
    )?;

    Ok(())

}
```

## Notes for development
### Communication architecture
Current communication architecture is strictly bounded request-response together. It would be
much better to have these two interactions separated into queues and adding one producer for writes and one consumer
for reads.

Without this functionality the services like ReadDataByPeriodicIdentifier cannot be implemented.

### Hierarchy

module __uds__ - top module containing UdsClient trough which all interaction is provided for the user
services used by UdsClient are stored in separate modules - see for example read_data_by_identifier.rs,
where structure of service module is described

module __communication__ - basic communication framework. Purpose of this module is to provide send
and receive functionality for UdsClient.

### Services implementation
each service consists of three steps  
__compose function__ - serializing service method arguments and other needed
data to Vec\<u8\>  
__send and receive__ - passing composed vector as slice to the communication backend and returning raw response  
__parse function__ - parsing received raw response &\[u8\] and serializing it into UdsMessage

## Notes
For the correct behaviour, you need to have Linux kernel with applied patch:
<https://lore.kernel.org/linux-can/20230818114345.142983-1-lukas.magel@posteo.net/#r>

<!-- cargo-rdme end -->
<!-- FROM HERE ON YOU CAN EDIT THE TEXT -->

## License
This project is licensed under the MIT License - see the LICENSE file for details.