Skip to content

Commit

Permalink
WIP: Add Peak CAN driver
Browse files Browse the repository at this point in the history
  • Loading branch information
Notgnoshi committed Aug 8, 2023
1 parent 2e52a5c commit b2a4a69
Show file tree
Hide file tree
Showing 5 changed files with 270 additions and 10 deletions.
5 changes: 4 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ keywords = ["agriculture", "can", "canbus", "isobus", "j1939", "agritech", "smar

[dependencies]
rand = "0.8.5"
pcan-basic = { version = "1.0.2", optional = true }
socketcan = { version = "2.0.0", optional = true }
tracing = { version = "0.1.37", optional = true }

Expand All @@ -17,6 +18,8 @@ default = []
socketcan = ["dep:socketcan"]
# Optional logging instrumentation
tracing = ["dep:tracing"]
# Peak driver
peak = ["dep:pcan-basic"]

[dev-dependencies]
clap = { version = "4.3.19", features = ["derive"] }
Expand All @@ -25,4 +28,4 @@ tracing-subscriber = "0.3.17"

[[example]]
name = "forward"
required-features = ["socketcan", "tracing"]
required-features = ["tracing"]
71 changes: 62 additions & 9 deletions examples/forward.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,46 @@

use std::sync::mpsc::channel;

use ag_iso_stack::driver::{Driver, Frame, SocketcanDriver};
#[cfg(feature = "peak")]
use ag_iso_stack::driver::PeakDriver;
#[cfg(feature = "socketcan")]
use ag_iso_stack::driver::SocketcanDriver;
use ag_iso_stack::driver::{Driver, Frame};
use ag_iso_stack::tracing;
use clap::Parser;
use clap::{Parser, ValueEnum};
#[cfg(feature = "peak")]
use pcan_basic::bus::UsbBus;

fn parse_usb_bus(s: &str) -> Option<UsbBus> {
let s = s.to_uppercase();
match s.as_str() {
"USB1" => Some(UsbBus::USB1),
"USB2" => Some(UsbBus::USB2),
"USB3" => Some(UsbBus::USB3),
"USB4" => Some(UsbBus::USB4),
"USB5" => Some(UsbBus::USB5),
"USB6" => Some(UsbBus::USB6),
"USB7" => Some(UsbBus::USB7),
"USB8" => Some(UsbBus::USB8),
"USB9" => Some(UsbBus::USB9),
"USB10" => Some(UsbBus::USB10),
"USB11" => Some(UsbBus::USB11),
"USB12" => Some(UsbBus::USB12),
"USB13" => Some(UsbBus::USB13),
"USB14" => Some(UsbBus::USB14),
"USB15" => Some(UsbBus::USB15),
"USB16" => Some(UsbBus::USB16),
_ => None,
}
}

#[derive(Debug, Clone, ValueEnum)]
enum CanDriver {
#[cfg(feature = "socketcan")]
Socketcan,
#[cfg(feature = "peak")]
Pcan,
}

/// Forward CAN traffic from one interface to another
#[derive(Debug, Parser)]
Expand All @@ -25,13 +62,29 @@ struct Options {
/// Can be either a string interface name, or an integer interface index
#[clap(short, long, default_value_t = String::from("can1"))]
pub output_interface: String,

#[clap(short, long)]
pub driver: CanDriver,
}

fn create_driver(iface: &str) -> impl Driver {
if let Ok(index) = iface.parse::<u32>() {
SocketcanDriver::new_by_index(index)
} else {
SocketcanDriver::new_by_name(iface)
fn create_driver(iface: &str, driver: CanDriver) -> Box<dyn Driver> {
match driver {
#[cfg(feature = "socketcan")]
CanDriver::Socketcan => {
if let Ok(index) = iface.parse::<u32>() {
Box::new(SocketcanDriver::new_by_index(index))
} else {
Box::new(SocketcanDriver::new_by_name(iface))
}
}
#[cfg(feature = "peak")]
CanDriver::Pcan => {
let bus = parse_usb_bus(iface).unwrap();
let baud = ag_iso_stack::driver::Baudrate::Baud250K;
Box::new(PeakDriver::new(bus, baud))
}
#[allow(unreachable_patterns)]
_ => unreachable!(),
}
}

Expand All @@ -51,8 +104,8 @@ fn main() {
opts.output_interface
);

let mut input = create_driver(&opts.input_interface);
let mut output = create_driver(&opts.output_interface);
let mut input = create_driver(&opts.input_interface, opts.driver.clone());
let mut output = create_driver(&opts.output_interface, opts.driver);

input.open().unwrap();
output.open().unwrap();
Expand Down
12 changes: 12 additions & 0 deletions src/driver/driver.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,10 @@ use crate::driver::Frame;
pub enum DriverOpenError {
/// The driver failed to open with filesystem semantics
IoError(std::io::Error),
// TODO: Here and throughout. I don't love the pcan errors. They're not real std::error::Error
// types, and it's not obvious what they mean. Maybe we should re-think this error design?
#[cfg(feature = "peak")]
PeakError(pcan_basic::error::PcanError),
}

impl std::fmt::Display for DriverOpenError {
Expand All @@ -17,6 +21,10 @@ impl std::error::Error for DriverOpenError {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
match &self {
DriverOpenError::IoError(e) => Some(e),
// PcanError doesn't implement the Error trait
// DriverOpenError::PeakError(e) => Some(e),
#[allow(unreachable_patterns)]
_ => None,
}
}
}
Expand Down Expand Up @@ -49,6 +57,8 @@ pub enum DriverReadError {
ErrorFrame(),
/// The driver failed to read with filesystem semantics
IoError(std::io::Error),
#[cfg(feature = "peak")]
PeakError(pcan_basic::error::PcanError),
}

impl std::fmt::Display for DriverReadError {
Expand Down Expand Up @@ -79,6 +89,8 @@ pub enum DriverWriteError {
BusError(),
/// Some fault with filesystem semantics
IoError(std::io::Error),
#[cfg(feature = "peak")]
PeakError(pcan_basic::error::PcanError),
}

impl std::fmt::Display for DriverWriteError {
Expand Down
5 changes: 5 additions & 0 deletions src/driver/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,9 @@ mod driver;
mod frame;
mod pgn;

#[cfg(feature = "peak")]
mod peak;

#[cfg(feature = "socketcan")]
mod socketcan;

Expand All @@ -21,5 +24,7 @@ pub use driver::{Driver, DriverCloseError, DriverOpenError, DriverReadError, Dri
pub use frame::{Channel, Frame};
pub use pgn::Pgn;

#[cfg(feature = "peak")]
pub use self::peak::{Baudrate, PeakDriver};
#[cfg(feature = "socketcan")]
pub use self::socketcan::SocketcanDriver;
187 changes: 187 additions & 0 deletions src/driver/peak.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,187 @@
pub use pcan_basic::bus::UsbBus;
// NOTE: PcanError is undocumented in the Rust source - you should go to
// https://github.com/tsabelmann/pcan-basic-sys/blob/master/header/PCANBasic.h for error
// documentation.
//
// PcanError::XmtFull - controller transmit buffer full
// PcanError::Overrun - controller was read too late
// PcanError::BusLight - error counter reached the light limit
// PcanError::BusHeavy - error counter reached the heavy limit
// PcanError::BusPassive
// PcanError::BusOff
// PcanError::AnyBusErr
// PcanError::QrcvEmpty - receive queue empty
// PcanError::QOverrun - receive queue overrun
// PcanError::QxmtFull - transmit queue full
// PcanError::RegTest - controller hardware registers test failed
// PcanError::NoDriver - driver not loaded
// PcanError::HwInUse - Hardware already in use by a network
// PcanError::NetInUse - A client is already connected to the Net
// PcanError::IllHw - Hardware handle is invalid
// PcanError::IllNet - Net handle is invalid
// PcanError::IllClient - Client handle is invalid
// PcanError::Resource - Resource (FIFO, Client, timeout) cannot be created
// PcanError::IllParamType - Invalid parameter
// PcanError::IllParamVal - Invalid parameter value
// PcanError::Unknown
// PcanError::IllData - Invalid data, function, or action
// PcanError::IllMode - Driver object state is wrong for the attempted operation
// PcanError::Caution - An opperation was successfully carried out, but irregularities were found
// PcanError::Initialize - Channel not initialized
// PcanError::IllOperation - Invalid operation
pub use pcan_basic::error::PcanError;
use pcan_basic::socket::usb::UsbCanSocket;
use pcan_basic::socket::{Baudrate as PcanBaudrate, RecvCan, SendCan};

use crate::driver::{
Driver, DriverCloseError, DriverOpenError, DriverReadError, DriverWriteError, Frame,
};
use crate::tracing;

#[derive(Debug, Clone, PartialEq, Eq)]
pub enum Baudrate {
Baud1M,
Baud800K,
Baud500K,
Baud250K,
Baud125K,
Baud100K,
Baud95K,
Baud83K,
Baud50K,
Baud47K,
Baud33K,
Baud20K,
Baud10K,
Baud5K,
}

// It's unfortunate that we have to duplicate pcan's baudrate enum, but it doesn't implement Clone
// or Debug, so we have to.
impl From<Baudrate> for PcanBaudrate {
fn from(b: Baudrate) -> PcanBaudrate {
match b {
Baudrate::Baud1M => PcanBaudrate::Baud1M,
Baudrate::Baud800K => PcanBaudrate::Baud800K,
Baudrate::Baud500K => PcanBaudrate::Baud500K,
Baudrate::Baud250K => PcanBaudrate::Baud250K,
Baudrate::Baud125K => PcanBaudrate::Baud125K,
Baudrate::Baud100K => PcanBaudrate::Baud100K,
Baudrate::Baud95K => PcanBaudrate::Baud95K,
Baudrate::Baud83K => PcanBaudrate::Baud83,
Baudrate::Baud50K => PcanBaudrate::Baud50K,
Baudrate::Baud47K => PcanBaudrate::Baud47K,
Baudrate::Baud33K => PcanBaudrate::Baud33K,
Baudrate::Baud20K => PcanBaudrate::Baud20K,
Baudrate::Baud10K => PcanBaudrate::Baud10K,
Baudrate::Baud5K => PcanBaudrate::Baud5K,
}
}
}

impl From<PcanError> for DriverOpenError {
fn from(e: PcanError) -> DriverOpenError {
DriverOpenError::PeakError(e)
}
}
impl From<PcanError> for DriverReadError {
fn from(e: PcanError) -> DriverReadError {
match e {
PcanError::QrcvEmpty => DriverReadError::NoFrameReady,
_ => DriverReadError::PeakError(e),
}
}
}
impl From<PcanError> for DriverWriteError {
fn from(e: PcanError) -> DriverWriteError {
match e {
PcanError::QxmtFull | PcanError::XmtFull | PcanError::Overrun => {
DriverWriteError::NotReady
}
_ => DriverWriteError::PeakError(e),
}
}
}

pub struct PeakDriver {
sock: Option<UsbCanSocket>,
bus: UsbBus,
baud: Baudrate,
}

impl PeakDriver {
pub fn new(bus: UsbBus, baud: Baudrate) -> Self {
Self {
sock: None,
bus,
baud,
}
}
}

impl Driver for PeakDriver {
fn is_valid(&self) -> bool {
self.sock.is_some()
}
fn open(&mut self) -> Result<(), DriverOpenError> {
// pcan's Baudrate type doesn't implement Clone, so you can't pass it to open() behind the
// &mut self mutable reference.
match UsbCanSocket::open(self.bus, self.baud.clone().into()) {
Ok(sock) => {
tracing::info!("Opened {:?} @ {:?}", self.bus, self.baud);
self.sock = Some(sock);
Ok(())
}
Err(e) => {
let e = e.into();
tracing::error!("Failed to open {:?} @ {:?}: {e:?}", self.bus, self.baud);
Err(e)
}
}
}
fn close(&mut self) -> Result<(), DriverCloseError> {
self.sock = None;
Ok(())
}

fn read_nonblocking(&mut self, _frame: &mut Frame) -> Result<(), DriverReadError> {
let Some(sock) = self.sock.as_mut() else {
tracing::warn!("Tried to read from closed interface {:?}", self.bus);
return Err(DriverReadError::DriverClosed);
};

match sock.recv() {
Ok((_frame, _timestamp)) => {
tracing::trace!("Received {_frame:?} @ {_timestamp:?}");
Ok(())
}
Err(PcanError::QrcvEmpty) => Err(DriverReadError::NoFrameReady),
Err(e) => {
tracing::error!("Error receiving frame: {e:?}");
Err(e.into())
}
}
}

fn write_nonblocking(&mut self, _frame: &Frame) -> Result<(), DriverWriteError> {
let Some(sock) = self.sock.as_mut() else {
tracing::warn!("Tried to write to closed interface {:?}", self.bus);
return Err(DriverWriteError::DriverClosed);
};

let pcan_frame = pcan_basic::socket::CanFrame::default();
match sock.send(pcan_frame) {
Ok(_) => {
tracing::trace!("Wrote frame: {_frame:?}");
Ok(())
}
Err(e @ (PcanError::QxmtFull | PcanError::XmtFull | PcanError::Overrun)) => {
Err(e.into())
}
Err(e) => {
tracing::error!("Error: {e:?} writing frame: {_frame:?}");
Err(e.into())
}
}
}
}

0 comments on commit b2a4a69

Please sign in to comment.