Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Clean up API and Errors #49

Merged
merged 2 commits into from
Mar 20, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ Welcome to the `automotive` crate documentation. The purpose of this crate is to
The following adapter opens the first available adapter on the system, and then receives all frames.

```rust
let adapter = automotive::adapter::get_adapter().unwrap();
let adapter = automotive::can::get_adapter().unwrap();
let mut stream = adapter.recv();

while let Some(frame) = stream.next().await {
Expand All @@ -23,7 +23,7 @@ while let Some(frame) = stream.next().await {
The automotive crate also supplies interfaces for various diagnostic protocols such as UDS. The adapter is first wrapped to support the ISO Transport Layer, then a UDS Client is created. All methods are fully async, making it easy to communicate with multiple ECUs in parallel. See [https://github.com/I-CAN-hack/automotive/issues/21](https://github.com/I-CAN-hack/automotive/issues/21) for progress on the supported SIDs.

```rust
let adapter = automotive::adapter::get_adapter().unwrap();
let adapter = automotive::can::get_adapter().unwrap();
let isotp = automotive::isotp::IsoTPAdapter::from_id(&adapter, 0x7a1);
let uds = automotive::uds::UDSClient::new(&isotp);

Expand Down
2 changes: 1 addition & 1 deletion examples/can_printer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ use tracing_subscriber;
async fn main() {
tracing_subscriber::fmt::init();

let adapter = automotive::adapter::get_adapter().unwrap();
let adapter = automotive::can::get_adapter().unwrap();
let mut stream = adapter.recv();

while let Some(frame) = stream.next().await {
Expand Down
2 changes: 1 addition & 1 deletion examples/isotp.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ use tracing_subscriber;
async fn main() {
tracing_subscriber::fmt::init();

let adapter = automotive::adapter::get_adapter().unwrap();
let adapter = automotive::can::get_adapter().unwrap();
let config = IsoTPConfig::new(0, Identifier::Standard(0x7a1));
let isotp = IsoTPAdapter::new(&adapter, config);

Expand Down
8 changes: 4 additions & 4 deletions examples/query_fw_versions.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
use automotive::async_can::AsyncCanAdapter;
use automotive::can::AsyncCanAdapter;
use automotive::can::Identifier;
use automotive::error::Error;
use automotive::isotp::{IsoTPAdapter, IsoTPConfig};
use automotive::Error;

use automotive::uds::constants::DataIdentifier;
use automotive::uds::DataIdentifier;
use automotive::uds::UDSClient;

use bstr::ByteSlice;
Expand Down Expand Up @@ -43,7 +43,7 @@ async fn get_version(adapter: &AsyncCanAdapter, identifier: u32) -> Result<(), E
async fn main() {
tracing_subscriber::fmt::init();

let adapter = automotive::adapter::get_adapter().unwrap();
let adapter = automotive::can::get_adapter().unwrap();

let standard_ids = 0x700..=0x7ff;
let extended_ids = (0x00..=0xff).map(|i| 0x18da0000 + (i << 8) + 0xf1);
Expand Down
4 changes: 2 additions & 2 deletions examples/uds.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
use automotive::uds::constants::{DataIdentifier, SessionType};
use automotive::uds::{DataIdentifier, SessionType};
use bstr::ByteSlice;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
tracing_subscriber::fmt::init();

let adapter = automotive::adapter::get_adapter()?;
let adapter = automotive::can::get_adapter()?;
let isotp = automotive::isotp::IsoTPAdapter::from_id(&adapter, 0x7a1);
let uds = automotive::uds::UDSClient::new(&isotp);

Expand Down
2 changes: 1 addition & 1 deletion src/adapter.rs → src/can/adapter.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
//! Convenience functions to get a CAN adapter.

/// Convenience function to get the first available adapter on the system. Supports both comma.ai panda, and SocketCAN.
pub fn get_adapter() -> Result<crate::async_can::AsyncCanAdapter, crate::error::Error> {
pub fn get_adapter() -> Result<crate::can::AsyncCanAdapter, crate::error::Error> {
if let Ok(panda) = crate::panda::Panda::new_async() {
return Ok(panda);
}
Expand Down
File renamed without changes.
6 changes: 6 additions & 0 deletions src/can.rs → src/can/mod.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,13 @@
//! Generic CAN types and traits

pub mod adapter;
pub mod async_can;

use std::fmt;

pub use adapter::get_adapter;
pub use async_can::AsyncCanAdapter;

pub static DLC_TO_LEN: &[usize] = &[0, 1, 2, 3, 4, 5, 6, 7, 8, 12, 16, 20, 24, 32, 48, 64];

/// Identifier for a CAN frame
Expand Down
6 changes: 3 additions & 3 deletions src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,13 @@ pub enum Error {
#[error("Timeout")]
Timeout,
#[error(transparent)]
IsoTPError(#[from] crate::isotp::error::Error),
IsoTPError(#[from] crate::isotp::Error),
#[error(transparent)]
LibUsbError(#[from] rusb::Error),
#[error(transparent)]
PandaError(#[from] crate::panda::error::Error),
PandaError(#[from] crate::panda::Error),
#[error(transparent)]
UDSError(#[from] crate::uds::error::Error),
UDSError(#[from] crate::uds::Error),
}

impl From<tokio_stream::Elapsed> for Error {
Expand Down
51 changes: 25 additions & 26 deletions src/isotp/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
//! ```rust
//! use futures_util::stream::StreamExt;
//! async fn isotp_example() {
//! let adapter = automotive::adapter::get_adapter().unwrap();
//! let adapter = automotive::can::get_adapter().unwrap();
//! let config = automotive::isotp::IsoTPConfig::new(0, automotive::can::Identifier::Standard(0x7a1));
//! let isotp = automotive::isotp::IsoTPAdapter::new(&adapter, config);
//!
Expand All @@ -13,17 +13,16 @@
//! }
//! ```

pub mod constants;
pub mod error;
pub mod types;
mod constants;
mod error;
mod types;

use crate::async_can::AsyncCanAdapter;
use crate::can::{Frame, Identifier, DLC_TO_LEN};
use crate::error::Error;
use crate::isotp::constants::FlowStatus;
use crate::isotp::constants::FLOW_SATUS_MASK;
use crate::isotp::constants::{FrameType, FRAME_TYPE_MASK};
pub use constants::{FlowStatus, FrameType, FLOW_SATUS_MASK, FRAME_TYPE_MASK};
pub use error::Error;

use crate::can::AsyncCanAdapter;
use crate::can::{Frame, Identifier, DLC_TO_LEN};
use crate::Result;
use async_stream::stream;
use futures_core::stream::Stream;
use tokio_stream::{StreamExt, Timeout};
Expand Down Expand Up @@ -175,7 +174,7 @@ impl<'a> IsoTPAdapter<'a> {
}

/// Build a CAN frame from the payload. Inserts extended address and padding if needed.
fn frame(&self, data: &[u8]) -> Result<Frame, Error> {
fn frame(&self, data: &[u8]) -> Result<Frame> {
let mut data = data.to_vec();

if let Some(ext_address) = self.config.ext_address {
Expand All @@ -185,7 +184,7 @@ impl<'a> IsoTPAdapter<'a> {
// Check if the data length is valid
if !DLC_TO_LEN.contains(&data.len()) {
println!("len {}", data.len());
return Err(crate::error::Error::MalformedFrame);
return Err(crate::Error::MalformedFrame);
}

let frame = Frame {
Expand All @@ -199,7 +198,7 @@ impl<'a> IsoTPAdapter<'a> {
Ok(frame)
}

pub async fn send_single_frame(&self, data: &[u8]) -> Result<(), Error> {
pub async fn send_single_frame(&self, data: &[u8]) -> Result<()> {
let mut buf;

if data.len() < 0xf {
Expand All @@ -220,7 +219,7 @@ impl<'a> IsoTPAdapter<'a> {
Ok(())
}

pub async fn send_first_frame(&self, data: &[u8]) -> Result<usize, Error> {
pub async fn send_first_frame(&self, data: &[u8]) -> Result<usize> {
let mut buf;
if data.len() <= ISO_TP_MAX_DLEN {
let b0: u8 = FrameType::First as u8 | ((data.len() >> 8) & 0xF) as u8;
Expand All @@ -242,7 +241,7 @@ impl<'a> IsoTPAdapter<'a> {
Ok(offset)
}

pub async fn send_consecutive_frame(&self, data: &[u8], idx: usize) -> Result<(), Error> {
pub async fn send_consecutive_frame(&self, data: &[u8], idx: usize) -> Result<()> {
let idx = ((idx + 1) & 0xF) as u8;

let mut buf = vec![FrameType::Consecutive as u8 | idx];
Expand All @@ -261,7 +260,7 @@ impl<'a> IsoTPAdapter<'a> {
async fn receive_flow_control(
&self,
stream: &mut std::pin::Pin<&mut Timeout<impl Stream<Item = Frame>>>,
) -> Result<FlowControlConfig, Error> {
) -> Result<FlowControlConfig> {
for _ in 0..MAX_WAIT_FC {
let mut frame = stream.next().await.unwrap()?;

Expand Down Expand Up @@ -296,7 +295,7 @@ impl<'a> IsoTPAdapter<'a> {
Err(crate::isotp::error::Error::TooManyFCWait.into())
}

async fn send_multiple(&self, data: &[u8]) -> Result<(), Error> {
async fn send_multiple(&self, data: &[u8]) -> Result<()> {
// Stream for receiving flow control
let stream = self
.adapter
Expand Down Expand Up @@ -346,7 +345,7 @@ impl<'a> IsoTPAdapter<'a> {
}

/// Asynchronously send an ISO-TP frame of up to 4095 bytes. Returns [`Error::Timeout`] if the ECU is not responding in time with flow control messages.
pub async fn send(&self, data: &[u8]) -> Result<(), Error> {
pub async fn send(&self, data: &[u8]) -> Result<()> {
debug!("TX {}", hex::encode(data));

// Single frame has 1 byte of overhead for CAN, and 2 bytes for CAN-FD with escape sequence
Expand All @@ -364,7 +363,7 @@ impl<'a> IsoTPAdapter<'a> {
Ok(())
}

async fn recv_single_frame(&self, data: &[u8]) -> Result<Vec<u8>, Error> {
async fn recv_single_frame(&self, data: &[u8]) -> Result<Vec<u8>> {
let mut len = (data[0] & 0xF) as usize;
let mut offset = 1;

Expand All @@ -384,7 +383,7 @@ impl<'a> IsoTPAdapter<'a> {
Ok(data[offset..len + offset].to_vec())
}

async fn recv_first_frame(&self, data: &[u8], buf: &mut Vec<u8>) -> Result<usize, Error> {
async fn recv_first_frame(&self, data: &[u8], buf: &mut Vec<u8>) -> Result<usize> {
let b0 = data[0] as u16;
let b1 = data[1] as u16;
let mut len = ((b0 << 8 | b1) & 0xFFF) as usize;
Expand Down Expand Up @@ -422,7 +421,7 @@ impl<'a> IsoTPAdapter<'a> {
buf: &mut Vec<u8>,
len: usize,
idx: u8,
) -> Result<u8, Error> {
) -> Result<u8> {
let msg_idx = data[0] & 0xF;
let remaining_len = len - buf.len();

Expand Down Expand Up @@ -462,7 +461,7 @@ impl<'a> IsoTPAdapter<'a> {
async fn recv_from_stream(
&self,
stream: &mut std::pin::Pin<&mut Timeout<impl Stream<Item = Frame>>>,
) -> Result<Vec<u8>, Error> {
) -> Result<Vec<u8>> {
let mut buf = Vec::new();
let mut len: Option<usize> = None;
let mut idx: u8 = 1;
Expand All @@ -478,7 +477,7 @@ impl<'a> IsoTPAdapter<'a> {
Some(FrameType::First) => {
// If we already received a first frame, something went wrong
if len.is_some() {
return Err(Error::IsoTPError(crate::isotp::error::Error::OutOfOrder));
return Err(Error::OutOfOrder.into());
}
len = Some(self.recv_first_frame(data, &mut buf).await?);
}
Expand All @@ -491,20 +490,20 @@ impl<'a> IsoTPAdapter<'a> {
return Ok(buf);
}
} else {
return Err(Error::IsoTPError(crate::isotp::error::Error::OutOfOrder));
return Err(Error::OutOfOrder.into());
}
}
Some(FrameType::FlowControl) => {} // Ignore flow control frames, these are from a simultaneous transmission
_ => {
return Err(crate::isotp::error::Error::UnknownFrameType.into());
return Err(Error::UnknownFrameType.into());
}
};
}
unreachable!();
}

/// Stream of ISO-TP packets. Can be used if multiple responses are expected from a single request. Returns [`Error::Timeout`] if the timeout is exceeded between individual ISO-TP frames. Note the total time to receive a packet may be longer than the timeout.
pub fn recv(&self) -> impl Stream<Item = Result<Vec<u8>, Error>> + '_ {
pub fn recv(&self) -> impl Stream<Item = Result<Vec<u8>>> + '_ {
let stream = self
.adapter
.recv_filter(|frame| {
Expand Down
13 changes: 7 additions & 6 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
//! ```rust
//! use futures_util::stream::StreamExt;
//! async fn can_example() {
//! let adapter = automotive::adapter::get_adapter().unwrap();
//! let adapter = automotive::can::get_adapter().unwrap();
//! let mut stream = adapter.recv();
//!
//! while let Some(frame) = stream.next().await {
Expand All @@ -24,12 +24,12 @@
//!
//! ```rust
//! async fn uds_example() {
//! let adapter = automotive::adapter::get_adapter().unwrap();
//! let adapter = automotive::can::get_adapter().unwrap();
//! let isotp = automotive::isotp::IsoTPAdapter::from_id(&adapter, 0x7a1);
//! let uds = automotive::uds::UDSClient::new(&isotp);
//!
//! uds.tester_present().await.unwrap();
//! let response = uds.read_data_by_identifier(automotive::uds::constants::DataIdentifier::ApplicationSoftwareIdentification as u16).await.unwrap();
//! let response = uds.read_data_by_identifier(automotive::uds::DataIdentifier::ApplicationSoftwareIdentification as u16).await.unwrap();
//!
//! println!("Application Software Identification: {}", hex::encode(response));
//! }
Expand All @@ -40,13 +40,14 @@
//! - comma.ai panda (all platforms)
//!

pub mod adapter;
pub mod async_can;
pub mod can;
pub mod error;
mod error;
pub mod isotp;
pub mod panda;
pub mod uds;

pub use error::Error;
pub type Result<T> = std::result::Result<T, Error>;

#[cfg(target_os = "linux")]
pub mod socketcan;
Loading
Loading