use std::ffi::c_int;
use std::time::{Duration, Instant};
use std::u32;
use crate::adapter::Adapter;
use crate::frame::{CanFrame, Frame};
use crate::id;
mod bind;
use bind::{TPCANMsg, TPCANTimestamp, libPCBUSB};
use super::{AdapterBaud, AdapterError};
type PcanChannel = u16;
type PcanBaud = u16;
pub struct PcanAdapter {
lib: libPCBUSB,
channel: PcanChannel,
fd: c_int,
}
fn try_as_channel(name: &str) -> Result<PcanChannel, AdapterError> {
Ok(match name {
"" | "1" => bind::PCAN_USBBUS1 as PcanChannel,
"2" => bind::PCAN_USBBUS2 as PcanChannel,
"3" => bind::PCAN_USBBUS3 as PcanChannel,
"4" => bind::PCAN_USBBUS4 as PcanChannel,
"5" => bind::PCAN_USBBUS5 as PcanChannel,
"6" => bind::PCAN_USBBUS6 as PcanChannel,
"7" => bind::PCAN_USBBUS7 as PcanChannel,
"8" => bind::PCAN_USBBUS8 as PcanChannel,
_ => return Err(AdapterError::UnknownDevice),
})
}
fn try_as_baud(baud: u32) -> Result<PcanBaud, AdapterError> {
Ok(match baud {
10_000 => bind::PCAN_BAUD_10K as PcanBaud,
20_000 => bind::PCAN_BAUD_20K as PcanBaud,
50_000 => bind::PCAN_BAUD_50K as PcanBaud,
100_000 => bind::PCAN_BAUD_100K as PcanBaud,
125_000 => bind::PCAN_BAUD_125K as PcanBaud,
250_000 => bind::PCAN_BAUD_250K as PcanBaud,
500_000 => bind::PCAN_BAUD_500K as PcanBaud,
800_000 => bind::PCAN_BAUD_800K as PcanBaud,
1_000_000 => bind::PCAN_BAUD_1M as PcanBaud,
_ => return Err(AdapterError::UnsupportedBaud),
})
}
impl Default for TPCANMsg {
fn default() -> Self {
Self {
ID: 0,
MSGTYPE: 0,
LEN: 0,
DATA: [0; 8],
}
}
}
impl Default for TPCANTimestamp {
fn default() -> Self {
Self {
millis: 0,
millis_overflow: 0,
micros: 0,
}
}
}
impl PcanAdapter {
pub fn new(
name: &str,
baud: AdapterBaud,
) -> Result<Self, Box<dyn std::error::Error>> {
#[cfg(target_os = "macos")]
let lib = unsafe { libPCBUSB::new("libPCBUSB.dylib") }?;
#[cfg(target_os = "windows")]
let lib = unsafe { libPCBUSB::new("PCANBasic.dll") }?;
let channel = try_as_channel(name)?;
let status = unsafe {
lib.CAN_Initialize(
channel,
try_as_baud(baud)?,
bind::PCAN_USB as u8,
0,
0,
)
};
match status {
0 => {
let mut fd: c_int = 0;
let info_ptr: *mut c_int = &mut fd;
match unsafe {
lib.CAN_GetValue(
channel,
bind::PCAN_RECEIVE_EVENT as u8,
info_ptr as *mut ::std::os::raw::c_void,
4,
)
} {
bind::PCAN_ERROR_OK => Ok(Self { lib, fd, channel }),
_ => Err(Box::new(AdapterError::OpenFailed)),
}
}
_ => Err(Box::new(AdapterError::OpenFailed)),
}
}
}
impl Drop for PcanAdapter {
fn drop(&mut self) {
unsafe { self.lib.CAN_Uninitialize(self.channel.into()) };
}
}
impl Adapter for PcanAdapter {
fn send(&self, frame: &CanFrame) -> Result<(), Box<AdapterError>> {
let mut data: [u8; 8] = [0u8; 8];
data[..frame.dlc()].copy_from_slice(frame.data());
let mut msg = bind::TPCANMsg {
ID: id::raw_u32(frame.id()),
MSGTYPE: if frame.is_extended() {
bind::PCAN_MODE_EXTENDED
} else {
bind::PCAN_MODE_STANDARD
} as u8,
LEN: frame.dlc() as u8,
DATA: data,
};
let status = unsafe {
self.lib
.CAN_Write(self.channel.into(), &mut msg as *mut bind::TPCANMsg)
};
match status {
0 => Ok(()),
_ => Err(Box::new(AdapterError::WriteFailed)),
}
}
#[cfg(not(target_os = "windows"))]
fn recv(
&self,
timeout: Option<Duration>,
) -> Result<CanFrame, Box<AdapterError>> {
use nix::poll::{PollFd, PollFlags, PollTimeout, poll};
use std::os::unix::io::BorrowedFd;
let start_time = Instant::now();
let mut msg = bind::TPCANMsg::default();
let mut timestamp = TPCANTimestamp::default();
loop {
let status = unsafe {
self.lib.CAN_Read(
self.channel.into(),
&mut msg as *mut bind::TPCANMsg,
&mut timestamp as *mut bind::TPCANTimestamp,
)
};
match status {
bind::PCAN_ERROR_OK => {
let id = match msg.MSGTYPE as u32 {
bind::PCAN_MODE_STANDARD => {
id::new_standard(msg.ID as u16)
}
bind::PCAN_MODE_EXTENDED => id::new_extended(msg.ID),
_ => return Err(Box::new(AdapterError::ReadFailed)),
}
.ok_or(Box::new(AdapterError::ReadFailed))?;
let frame =
CanFrame::new(id, &msg.DATA[..msg.LEN as usize])
.ok_or(Box::new(AdapterError::ReadFailed))?;
return Ok(frame);
}
bind::PCAN_ERROR_QRCVEMPTY => {
let poll_interval = match timeout {
Some(t) => {
let now = Instant::now();
let end = start_time + t;
if now >= end {
return Err(Box::new(
AdapterError::ReadTimeout,
));
}
let remaining = (end - now).as_millis();
if remaining < 1000 {
PollTimeout::from(remaining as u16)
} else {
PollTimeout::from(1000u16)
}
}
None => PollTimeout::from(1000u16),
};
let pollfd = PollFd::new(
unsafe { BorrowedFd::borrow_raw(self.fd) },
PollFlags::POLLIN,
);
if poll(&mut [pollfd], poll_interval).is_err() {
return Err(Box::new(AdapterError::ReadFailed));
}
}
_ => return Err(Box::new(AdapterError::ReadFailed)),
}
}
}
#[cfg(target_os = "windows")]
fn recv(
&self,
timeout: Option<Duration>,
) -> Result<CanFrame, Box<AdapterError>> {
let start_time = Instant::now();
let mut msg = bind::TPCANMsg::default();
let mut timestamp = TPCANTimestamp::default();
loop {
let status = unsafe {
self.lib.CAN_Read(
self.channel.into(),
&mut msg as *mut bind::TPCANMsg,
&mut timestamp as *mut bind::TPCANTimestamp,
)
};
match status {
bind::PCAN_ERROR_OK => {
let id = match msg.MSGTYPE as u32 {
bind::PCAN_MODE_STANDARD => {
id::new_standard(msg.ID as u16)
}
bind::PCAN_MODE_EXTENDED => id::new_extended(msg.ID),
_ => return Err(Box::new(AdapterError::ReadFailed)),
}
.ok_or(Box::new(AdapterError::ReadFailed))?;
let frame =
CanFrame::new(id, &msg.DATA[..msg.LEN as usize])
.ok_or(Box::new(AdapterError::ReadFailed))?;
return Ok(frame);
}
bind::PCAN_ERROR_QRCVEMPTY => {
if let Some(timeout) = timeout {
std::thread::sleep(Duration::from_micros(10));
if Instant::now() - start_time >= timeout {
return Err(Box::new(AdapterError::ReadTimeout));
}
}
}
_ => return Err(Box::new(AdapterError::ReadFailed)),
}
}
}
}