From ba4b56dd01e5789bcd90e900c1ba65fb023fdcf4 Mon Sep 17 00:00:00 2001 From: Ashcon Mohseninia Date: Tue, 23 Jul 2024 17:17:50 +0200 Subject: [PATCH] 0.96.0 - Implement shared hardware type and simplify errors --- Cargo.toml | 2 +- examples/README.md | 5 ++- src/channel.rs | 9 ------ src/hardware/mod.rs | 77 ++++++++++++++++++++++++++++++++++++++++++++- 4 files changed, 81 insertions(+), 12 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 3765ad5..5c775ae 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "ecu_diagnostics" -version = "0.95.8" +version = "0.96.0" authors = ["Ashcon Mohseninia "] edition = "2021" description = "A rust crate for ECU diagnostic servers and communication APIs" diff --git a/examples/README.md b/examples/README.md index 5cf1391..5354b11 100644 --- a/examples/README.md +++ b/examples/README.md @@ -1,3 +1,6 @@ # Examples section -Each example here demonstates some feature of the ecu_diagnostic crate! \ No newline at end of file +Each example here demonstates some feature of the ecu_diagnostic crate! + +* kwp - Example of KWP Diagnostic server +* Peak - Example showing CAN Frame reading using PCAN-USB hardware + PCAN API under windows \ No newline at end of file diff --git a/src/channel.rs b/src/channel.rs index 4035cde..25cf216 100644 --- a/src/channel.rs +++ b/src/channel.rs @@ -58,15 +58,6 @@ impl From> for ChannelError { } } -impl From> for HardwareError { - fn from(_x: PoisonError) -> Self { - HardwareError::APIError { - code: 99, - desc: "PoisonError".into(), - } - } -} - impl From for HardwareError { fn from(e: mpsc::RecvError) -> Self { HardwareError::APIError { diff --git a/src/hardware/mod.rs b/src/hardware/mod.rs index 5293f1f..901773e 100644 --- a/src/hardware/mod.rs +++ b/src/hardware/mod.rs @@ -9,6 +9,7 @@ pub mod passthru; // Not finished at all yet, hide from the crate #[cfg(feature = "passthru")] use std::sync::Arc; +use std::{any::{Any, TypeId}, fmt::Debug, sync::{Mutex, PoisonError, RwLock}}; #[cfg(all(feature="socketcan", target_os="linux"))] pub mod socketcan; @@ -21,7 +22,7 @@ pub type HardwareResult = Result; /// The hardware trait defines functions supported by all adapter types, /// as well as functions that can create abstracted communication channels /// that can be used in diagnostic servers -pub trait Hardware: Clone { +pub trait Hardware { /// Creates an ISO-TP channel on the devices. /// This channel will live for as long as the hardware trait. Upon being dropped, /// the channel will automatically be closed, if it has been opened. @@ -56,6 +57,71 @@ pub trait Hardware: Clone { fn is_connected(&self) -> bool; } +#[derive(Clone)] +/// This is a simple wrapper around the [Hardware] data type that allows it to be cloned +/// and shared between multiple threads. +pub struct SharedHardware { + info: HardwareInfo, + hw: Arc>> +} + +impl SharedHardware { + /// Creates a new SharedHardware resource. Allowing the inner hardware to be cloned and passed around. + /// + /// ## Panics + /// This function will panic if attempting to create a SharedHardware instance of a SharedHardware (Recursive creation) + pub fn new(t: Box) -> Self { + if t.as_ref().type_id() == TypeId::of::() { + 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> { + self.hw.write()?.create_iso_tp_channel() + } + + fn create_can_channel(&mut self) -> HardwareResult> { + 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 { + self.hw.write().ok()?.read_battery_voltage() + } + + fn read_ignition_voltage(&mut self) -> Option { + 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)] /// Device hardware info used by [HardwareScanner] pub struct HardwareInfo { @@ -112,6 +178,9 @@ pub enum HardwareError { /// Function called on device that has not yet been opened #[error("Device was not opened")] DeviceNotOpen, + #[error("Device locked by another thread")] + /// Used by the [SharedHardware] structure, Indicates that a shared resource is locked by another thread + DeviceLockError, /// Lib loading error #[cfg(feature = "passthru")] @@ -126,6 +195,12 @@ impl From for HardwareError { } } +impl From> for HardwareError { + fn from(_value: PoisonError) -> Self { + Self::DeviceLockError + } +} + /// Contains details about what communication protocols /// are supported by the physical hardware #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]