Switch to using macros for data retrieval commands
This commit is contained in:
		
							parent
							
								
									8d521ff46d
								
							
						
					
					
						commit
						fb35987b28
					
				
					 5 changed files with 251 additions and 121 deletions
				
			
		|  | @ -3,7 +3,7 @@ name = "obd2" | |||
| description = "Utility for reading data from a vehicle over OBD-II" | ||||
| license = "MIT OR Apache-2.0" | ||||
| repository = "https://github.com/rsammelson/obd2" | ||||
| version = "0.2.0-pre2" | ||||
| version = "0.2.0-pre3" | ||||
| edition = "2021" | ||||
| 
 | ||||
| [dependencies] | ||||
|  |  | |||
|  | @ -1,16 +1,42 @@ | |||
| use crate::{Error, Obd2Device, Result}; | ||||
| 
 | ||||
| use super::{Dtc, DtcsInfo, Obd2DataRetrieval}; | ||||
| use super::{Dtc, DtcsInfo}; | ||||
| 
 | ||||
| impl<T: Obd2Device> Obd2DataRetrieval for T { | ||||
|     fn get_vin(&mut self) -> Result<String> { | ||||
|         let mut result = self.obd_command(0x09, 0x02)?.pop().unwrap(); | ||||
| pub(crate) trait GetObd2Values<T> | ||||
| where | ||||
|     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
 | ||||
|     Ok(String::from_utf8(result)?) | ||||
| } | ||||
| 
 | ||||
|     fn get_dtc_info(&mut self) -> Result<Vec<DtcsInfo>> { | ||||
|         let result = self.obd_command(0x01, 0x01)?; | ||||
| pub(super) fn get_dtc_info<T: Obd2Device>(device: &mut T) -> Result<Vec<DtcsInfo>> { | ||||
|     let result = device.obd_command(0x01, 0x01)?; | ||||
| 
 | ||||
|     result | ||||
|         .iter() | ||||
|  | @ -19,11 +45,9 @@ impl<T: Obd2Device> Obd2DataRetrieval for T { | |||
|                 Ok(DtcsInfo { | ||||
|                     malfunction_indicator_light: (response[0] & 0x80) == 0x80, | ||||
|                     dtc_count: response[0] & 0x7f, | ||||
|                         common_test_availability: ((response[1] & 0xf0) >> 1) | ||||
|                             | (response[1] & 0x07), | ||||
|                     common_test_availability: ((response[1] & 0xf0) >> 1) | (response[1] & 0x07), | ||||
|                     is_compression_engine: (response[1] & 0x08) == 0x08, | ||||
|                         specific_test_availability: ((response[3] as u16) << 8) | ||||
|                             | (response[2] as u16), | ||||
|                     specific_test_availability: ((response[3] as u16) << 8) | (response[2] as u16), | ||||
|                 }) | ||||
|             } else { | ||||
|                 Err(Error::Other(format!( | ||||
|  | @ -35,8 +59,8 @@ impl<T: Obd2Device> Obd2DataRetrieval for T { | |||
|         .collect() | ||||
| } | ||||
| 
 | ||||
|     fn get_dtcs(&mut self) -> Result<Vec<Vec<Dtc>>> { | ||||
|         let result = self.obd_mode_command(0x03)?; | ||||
| pub(super) fn get_dtcs<T: Obd2Device>(device: &mut T) -> Result<Vec<Vec<Dtc>>> { | ||||
|     let result = device.obd_mode_command(0x03)?; | ||||
|     result | ||||
|         .iter() | ||||
|         .map(|response| match response.first() { | ||||
|  | @ -71,29 +95,3 @@ impl<T: Obd2Device> Obd2DataRetrieval for T { | |||
|         }) | ||||
|         .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
									
								
							
							
						
						
									
										119
									
								
								src/commands/macros.rs
									
										
									
									
									
										Normal 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) | ||||
|     }; | ||||
| } | ||||
|  | @ -1,55 +1,63 @@ | |||
| //! High level OBD-II interface
 | ||||
| 
 | ||||
| mod implementation; | ||||
| use implementation::GetObd2Values; | ||||
| 
 | ||||
| #[macro_use] | ||||
| mod macros; | ||||
| 
 | ||||
| mod types; | ||||
| use types::private; | ||||
| pub use types::{Dtc, DtcsInfo}; | ||||
| 
 | ||||
| use crate::Result; | ||||
| use crate::{Obd2Device, Result}; | ||||
| 
 | ||||
| func! { | ||||
|     /// Trait for devices that can retrieve data over OBD-II
 | ||||
|     ///
 | ||||
|     /// Automatically implemented for implementors of [Obd2Device](crate::Obd2Device).
 | ||||
| pub trait Obd2DataRetrieval: private::Sealed { | ||||
|     /// Check which getters are supported by the current vehicle
 | ||||
|     // fn get_support() -> Obd2FunctionSupport;
 | ||||
|     trait Obd2DataRetrieval; | ||||
| 
 | ||||
|     { | ||||
|         /// 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.
 | ||||
|     fn get_vin(&mut self) -> Result<String>; | ||||
| 
 | ||||
|     /// Get DTC (diagnostic trouble code) metadata for each ECU
 | ||||
|     fn get_dtc_info(&mut self) -> Result<Vec<DtcsInfo>>; | ||||
|         fn get_vin(self, 0x09, 0x02) -> Result<String> { | ||||
|             implementation::get_vin(self) | ||||
|         } | ||||
| 
 | ||||
|         /// 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)
 | ||||
|     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
 | ||||
|     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
 | ||||
|     ///
 | ||||
|     /// 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
 | ||||
|     ///
 | ||||
|     /// 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
 | ||||
|     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
 | ||||
|     fn get_speed(&mut self) -> Result<u8>; | ||||
| } | ||||
| 
 | ||||
| mod private { | ||||
|     pub trait Sealed {} | ||||
|     impl<T: crate::Obd2Device> Sealed for T {} | ||||
|     fn get_speed(0x01, 0x0D) -> u8; | ||||
| } | ||||
|  |  | |||
|  | @ -41,3 +41,8 @@ impl fmt::Display for Dtc { | |||
|         f.write_fmt(format_args!("{}{:03X}", c, n)) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| pub(super) mod private { | ||||
|     pub trait Sealed {} | ||||
|     impl<T: crate::Obd2Device> Sealed for T {} | ||||
| } | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Robert Sammelson
						Robert Sammelson