mod dpdu;
#[cfg(feature = "passthru")]
pub mod passthru;
use std::{any::{Any, TypeId}, fmt::Debug, sync::{Arc, PoisonError, RwLock}};
#[cfg(all(feature="socketcan", target_os="linux"))]
pub mod socketcan;
#[cfg(feature = "slcan")]
pub mod slcan;
#[cfg(feature = "pcan-usb")]
pub mod pcan_usb;
pub mod sw_isotp;
use crate::channel::{CanChannel, IsoTPChannel};
pub type HardwareResult<T> = Result<T, HardwareError>;
pub trait Hardware {
fn create_iso_tp_channel(&mut self) -> HardwareResult<Box<dyn IsoTPChannel>>;
fn create_can_channel(&mut self) -> HardwareResult<Box<dyn CanChannel>>;
fn is_iso_tp_channel_open(&self) -> bool;
fn is_can_channel_open(&self) -> bool;
fn read_battery_voltage(&mut self) -> Option<f32>;
fn read_ignition_voltage(&mut self) -> Option<f32>;
fn get_info(&self) -> &HardwareInfo;
fn is_connected(&self) -> bool;
}
#[derive(Clone)]
pub struct SharedHardware {
info: HardwareInfo,
hw: Arc<RwLock<Box<dyn Hardware>>>
}
impl SharedHardware {
pub fn new(t: Box<dyn Hardware>) -> Self {
if t.as_ref().type_id() == TypeId::of::<Self>() {
panic!("Attempting to create a SharedHardware instance of a SharedHardware!")
}
let info = t.get_info().clone();
Self {
info,
hw: Arc::new(RwLock::new(t))
}
}
}
impl Hardware for SharedHardware {
fn create_iso_tp_channel(&mut self) -> HardwareResult<Box<dyn IsoTPChannel>> {
self.hw.write()?.create_iso_tp_channel()
}
fn create_can_channel(&mut self) -> HardwareResult<Box<dyn CanChannel>> {
self.hw.write()?.create_can_channel()
}
fn is_iso_tp_channel_open(&self) -> bool {
self.hw.read().map(|x| x.is_iso_tp_channel_open()).unwrap_or(true)
}
fn is_can_channel_open(&self) -> bool {
self.hw.read().map(|x| x.is_can_channel_open()).unwrap_or(true)
}
fn read_battery_voltage(&mut self) -> Option<f32> {
self.hw.write().ok()?.read_battery_voltage()
}
fn read_ignition_voltage(&mut self) -> Option<f32> {
self.hw.write().ok()?.read_ignition_voltage()
}
fn get_info(&self) -> &HardwareInfo {
&self.info
}
fn is_connected(&self) -> bool {
self.hw.read().unwrap().is_connected()
}
}
impl Debug for SharedHardware {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_tuple("SharedHardware").finish()
}
}
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
pub struct HardwareInfo {
pub name: String,
pub vendor: Option<String>,
pub device_fw_version: Option<String>,
pub api_version: Option<String>,
pub library_version: Option<String>,
pub library_location: Option<String>,
pub capabilities: HardwareCapabilities,
}
pub trait HardwareScanner<T: Hardware> {
fn list_devices(&self) -> Vec<HardwareInfo>;
fn open_device_by_index(&self, idx: usize) -> HardwareResult<T>;
fn open_device_by_name(&self, name: &str) -> HardwareResult<T>;
}
#[derive(Clone, Debug, thiserror::Error)]
pub enum HardwareError {
#[error("Device library API error {code} ({desc})")]
APIError {
code: u32,
desc: String,
},
#[error("Channel type conflicts with an already open channel")]
ConflictingChannel,
#[error("Channel type not supported on this hardware")]
ChannelNotSupported,
#[error("Device not found")]
DeviceNotFound,
#[error("Device was not opened")]
DeviceNotOpen,
#[error("Device locked by another thread")]
DeviceLockError,
#[cfg(any(feature = "passthru", feature = "pcan-usb"))]
#[error("Device API library load error")]
LibLoadError(#[from] #[source] Arc<libloading::Error>),
}
#[cfg(any(feature = "passthru", feature = "pcan-usb"))]
impl From<libloading::Error> for HardwareError {
fn from(err: libloading::Error) -> Self {
Self::LibLoadError(Arc::new(err))
}
}
#[cfg(feature = "pcan-usb")]
impl From<pcan_usb::pcan_types::PCanErrorTy> for HardwareError {
fn from(err: pcan_usb::pcan_types::PCanErrorTy) -> Self {
Self::APIError { code: 99, desc: err.to_string() }
}
}
impl<T> From<PoisonError<T>> for HardwareError {
fn from(_value: PoisonError<T>) -> Self {
Self::DeviceLockError
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
pub struct HardwareCapabilities {
pub iso_tp: bool,
pub can: bool,
pub kline: bool,
pub kline_kwp: bool,
pub sae_j1850: bool,
pub sci: bool,
pub ip: bool,
}