Switch to using macros for data retrieval commands

This commit is contained in:
Robert Sammelson 2023-05-17 22:17:33 -04:00
parent 8d521ff46d
commit fb35987b28
No known key found for this signature in database
GPG key ID: 92F1F04EDB06B9E9
5 changed files with 251 additions and 121 deletions

View file

@ -3,7 +3,7 @@ name = "obd2"
description = "Utility for reading data from a vehicle over OBD-II" description = "Utility for reading data from a vehicle over OBD-II"
license = "MIT OR Apache-2.0" license = "MIT OR Apache-2.0"
repository = "https://github.com/rsammelson/obd2" repository = "https://github.com/rsammelson/obd2"
version = "0.2.0-pre2" version = "0.2.0-pre3"
edition = "2021" edition = "2021"
[dependencies] [dependencies]

View file

@ -1,16 +1,42 @@
use crate::{Error, Obd2Device, Result}; use crate::{Error, Obd2Device, Result};
use super::{Dtc, DtcsInfo, Obd2DataRetrieval}; use super::{Dtc, DtcsInfo};
impl<T: Obd2Device> Obd2DataRetrieval for T { pub(crate) trait GetObd2Values<T>
fn get_vin(&mut self) -> Result<String> { where
let mut result = self.obd_command(0x09, 0x02)?.pop().unwrap(); Self: Sized,
{
fn get_obd2_val(device: &mut T, service: u8, pid: u8) -> Result<Vec<Self>>;
}
impl<T: Obd2Device> GetObd2Values<T> for u8 {
fn get_obd2_val(device: &mut T, service: u8, pid: u8) -> Result<Vec<Self>> {
Ok(device
.obd_command_len::<1>(service, pid)?
.into_iter()
.map(|r| r[0])
.collect())
}
}
impl<T: Obd2Device> GetObd2Values<T> for u16 {
fn get_obd2_val(device: &mut T, service: u8, pid: u8) -> Result<Vec<Self>> {
Ok(device
.obd_command_len::<2>(service, pid)?
.into_iter()
.map(u16::from_be_bytes)
.collect())
}
}
pub(super) fn get_vin<T: Obd2Device>(device: &mut T) -> Result<String> {
let mut result = device.obd_command(0x09, 0x02)?.pop().unwrap();
result.remove(0); // do not know what this byte is result.remove(0); // do not know what this byte is
Ok(String::from_utf8(result)?) Ok(String::from_utf8(result)?)
} }
fn get_dtc_info(&mut self) -> Result<Vec<DtcsInfo>> { pub(super) fn get_dtc_info<T: Obd2Device>(device: &mut T) -> Result<Vec<DtcsInfo>> {
let result = self.obd_command(0x01, 0x01)?; let result = device.obd_command(0x01, 0x01)?;
result result
.iter() .iter()
@ -19,11 +45,9 @@ impl<T: Obd2Device> Obd2DataRetrieval for T {
Ok(DtcsInfo { Ok(DtcsInfo {
malfunction_indicator_light: (response[0] & 0x80) == 0x80, malfunction_indicator_light: (response[0] & 0x80) == 0x80,
dtc_count: response[0] & 0x7f, dtc_count: response[0] & 0x7f,
common_test_availability: ((response[1] & 0xf0) >> 1) common_test_availability: ((response[1] & 0xf0) >> 1) | (response[1] & 0x07),
| (response[1] & 0x07),
is_compression_engine: (response[1] & 0x08) == 0x08, is_compression_engine: (response[1] & 0x08) == 0x08,
specific_test_availability: ((response[3] as u16) << 8) specific_test_availability: ((response[3] as u16) << 8) | (response[2] as u16),
| (response[2] as u16),
}) })
} else { } else {
Err(Error::Other(format!( Err(Error::Other(format!(
@ -33,10 +57,10 @@ impl<T: Obd2Device> Obd2DataRetrieval for T {
} }
}) })
.collect() .collect()
} }
fn get_dtcs(&mut self) -> Result<Vec<Vec<Dtc>>> { pub(super) fn get_dtcs<T: Obd2Device>(device: &mut T) -> Result<Vec<Vec<Dtc>>> {
let result = self.obd_mode_command(0x03)?; let result = device.obd_mode_command(0x03)?;
result result
.iter() .iter()
.map(|response| match response.first() { .map(|response| match response.first() {
@ -70,30 +94,4 @@ impl<T: Obd2Device> Obd2DataRetrieval for T {
)), )),
}) })
.collect::<Result<Vec<Vec<Dtc>>>>() .collect::<Result<Vec<Vec<Dtc>>>>()
}
fn get_engine_load(&mut self) -> Result<u8> {
Ok(self.obd_command_cnt_len::<1, 1>(0x01, 0x0C)?[0][0])
}
fn get_engine_coolant_temperature(&mut self) -> Result<i16> {
Ok((self.obd_command_cnt_len::<1, 1>(0x01, 0x0C)?[0][0] as i16) - 40)
}
fn get_fuel_pressure(&mut self) -> Result<i16> {
todo!()
}
fn get_engine_manifold_pressure(&mut self) -> Result<f32> {
todo!()
}
fn get_rpm(&mut self) -> Result<f32> {
let result = self.obd_command_cnt_len::<1, 2>(0x01, 0x0C)?[0];
Ok(f32::from(u16::from_be_bytes(result)) / 4.0)
}
fn get_speed(&mut self) -> Result<u8> {
Ok(self.obd_command_cnt_len::<1, 1>(0x01, 0x0C)?[0][0])
}
} }

119
src/commands/macros.rs Normal file
View file

@ -0,0 +1,119 @@
macro_rules! trait_func {
{
$(#[$attr:meta])*
fn $name:ident($service:expr, $pid:expr) -> $retrieve_type:ty;
} => {
$(#[$attr])*
fn $name(&mut self) -> Result<Vec<$retrieve_type>>;
};
{
$(#[$attr:meta])*
fn $name:ident($service:expr, $pid:expr, $map:expr) -> $retrieve_type:ty;
} => {
$(#[$attr])*
fn $name(&mut self) -> Result<Vec<$retrieve_type>>;
};
{
$(#[$attr:meta])*
fn $name:ident<$retrieve_type:ty>($service:expr, $pid:expr, $map:expr) -> $out_type:ty;
} => {
$(#[$attr])*
fn $name(&mut self) -> Result<Vec<$out_type>>;
};
}
macro_rules! impl_func {
{
$(#[$attr:meta])*
fn $name:ident($service:expr, $pid:expr) -> $retrieve_type:ty;
} => {
fn $name(&mut self) -> Result<Vec<$retrieve_type>> {
<$retrieve_type>::get_obd2_val(self, $service, $pid)
}
};
{
$(#[$attr:meta])*
fn $name:ident($service:expr, $pid:expr, $map:expr) -> $retrieve_type:ty;
} => {
fn $name(&mut self) -> Result<Vec<$retrieve_type>> {
$map(<$retrieve_type>::get_obd2_val(self, $service, $pid))
}
};
{
$(#[$attr:meta])*
fn $name:ident<$retrieve_type:ty>($service:expr, $pid:expr, $map:expr) -> $out_type:ty;
} => {
fn $name(&mut self) -> Result<Vec<$out_type>> {
Ok(
<$retrieve_type>::get_obd2_val(self, $service, $pid)?
.into_iter()
.map(|v| $map(v.into()))
.collect()
)
}
};
}
macro_rules! func {
{
$(#[$attr:meta])*
trait $trait_name:ident;
$({
$(
$(#[$f_attr_inner:meta])*
fn $f_name:ident($self:ident, $f_service:expr$(, $f_pid:expr)?) -> $f_output:ty
$inside:block
)+
})?
$(
$(#[$attr_inner:meta])*
fn $name:ident$(<$retrieve_type:ty>)?($service:expr, $pid:expr$(, $map:expr)?) -> $output:ty;
)*
} => {
$(#[$attr])*
pub trait $trait_name: private::Sealed {
$($(
$(#[$f_attr_inner])*
fn $f_name(&mut self) -> $f_output;
)+)?
$(
trait_func! {
$(#[$attr_inner])*
///
#[doc=concat!(
"Details: service ", $service,
", PID ", $pid,
", read type: `", decode_type!($output $(, $retrieve_type)?), "`"
)]
fn $name$(<$retrieve_type>)?($service, $pid$(, $map)?) -> $output;
}
)*
}
impl<T: Obd2Device> $trait_name for T {
$($(
fn $f_name(&mut $self) -> $f_output
$inside
)+)?
$(
impl_func! {
$(#[$attr_inner])*
fn $name$(<$retrieve_type>)?($service, $pid$(, $map)?) -> $output;
}
)*
}
};
}
macro_rules! decode_type {
($_:ty, $t:ty) => {
stringify!($t)
};
($t:ty) => {
stringify!($t)
};
}

View file

@ -1,55 +1,63 @@
//! High level OBD-II interface //! High level OBD-II interface
mod implementation; mod implementation;
use implementation::GetObd2Values;
#[macro_use]
mod macros;
mod types; mod types;
use types::private;
pub use types::{Dtc, DtcsInfo}; pub use types::{Dtc, DtcsInfo};
use crate::Result; use crate::{Obd2Device, Result};
/// Trait for devices that can retrieve data over OBD-II func! {
/// /// Trait for devices that can retrieve data over OBD-II
/// Automatically implemented for implementors of [Obd2Device](crate::Obd2Device). ///
pub trait Obd2DataRetrieval: private::Sealed { /// Automatically implemented for implementors of [Obd2Device](crate::Obd2Device).
/// Check which getters are supported by the current vehicle trait Obd2DataRetrieval;
// fn get_support() -> Obd2FunctionSupport;
{
/// Retreive the VIN (vehicle identification number) /// Retreive the VIN (vehicle identification number)
/// ///
/// Service 0x09, PID 0x01. This should match the number printed on the vehicle, and is a good /// This should match the number printed on the vehicle, and is a good
/// command for checking that the OBD-II interface is working correctly. /// command for checking that the OBD-II interface is working correctly.
fn get_vin(&mut self) -> Result<String>; fn get_vin(self, 0x09, 0x02) -> Result<String> {
implementation::get_vin(self)
/// Get DTC (diagnostic trouble code) metadata for each ECU }
fn get_dtc_info(&mut self) -> Result<Vec<DtcsInfo>>;
/// Get DTCs for each ECU /// Get DTCs for each ECU
fn get_dtcs(&mut self) -> Result<Vec<Vec<Dtc>>>; fn get_dtcs(self, 0x03) -> Result<Vec<Vec<Dtc>>> {
implementation::get_dtcs(self)
}
/// Get DTC (diagnostic trouble code) metadata for each ECU
fn get_dtc_info(self, 0x01, 0x01) -> Result<Vec<DtcsInfo>> {
implementation::get_dtc_info(self)
}
}
/// Get the calculated engine load (out of 255) /// Get the calculated engine load (out of 255)
fn get_engine_load(&mut self) -> Result<u8>; fn get_engine_load(0x01, 0x04) -> u8;
/// Get the temperature of the engine's coolant in ºC /// Get the temperature of the engine's coolant in ºC
fn get_engine_coolant_temperature(&mut self) -> Result<i16>; fn get_engine_coolant_temperature<u8>(0x01, 0x05, |v: i16| v - 40) -> i16;
/// Get the fuel pressure in kPa /// Get the fuel pressure in kPa
/// ///
/// This measurement is gauge pressure (measured relative to the atmosphere) /// This measurement is gauge pressure (measured relative to the atmosphere)
fn get_fuel_pressure(&mut self) -> Result<i16>; fn get_fuel_pressure<u8>(0x01, 0x0A, |v: i16| v * 3) -> i16;
/// Get the intake manifold pressure in kPa /// Get the intake manifold pressure in kPa
/// ///
/// This measurement is absolute pressure. /// This measurement is absolute pressure.
fn get_engine_manifold_pressure(&mut self) -> Result<f32>; fn get_engine_manifold_pressure<u16>(0x01, 0x0B, |v: f32| v) -> f32;
/// Get the RPM in increments of 0.25 /// Get the RPM in increments of 0.25
fn get_rpm(&mut self) -> Result<f32>; fn get_rpm<u16>(0x01, 0x0C, |v: f32| v / 4.0) -> f32;
/// Get the speed in km/h /// Get the speed in km/h
fn get_speed(&mut self) -> Result<u8>; fn get_speed(0x01, 0x0D) -> u8;
}
mod private {
pub trait Sealed {}
impl<T: crate::Obd2Device> Sealed for T {}
} }

View file

@ -41,3 +41,8 @@ impl fmt::Display for Dtc {
f.write_fmt(format_args!("{}{:03X}", c, n)) f.write_fmt(format_args!("{}{:03X}", c, n))
} }
} }
pub(super) mod private {
pub trait Sealed {}
impl<T: crate::Obd2Device> Sealed for T {}
}