1use std::ffi::c_int;
4use std::time::{Duration, Instant};
5use std::u32;
6
7use crate::adapter::Adapter;
8use crate::frame::{CanFrame, Frame};
9use crate::id;
10
11mod bind;
12use bind::{TPCANMsg, TPCANTimestamp, libPCBUSB};
13
14use super::{AdapterBaud, AdapterError};
15
16type PcanChannel = u16;
17type PcanBaud = u16;
18
19pub struct PcanAdapter {
20 lib: libPCBUSB,
21 channel: PcanChannel,
22 fd: c_int,
23}
24
25fn try_as_channel(name: &str) -> Result<PcanChannel, AdapterError> {
26 Ok(match name {
27 "" | "1" => bind::PCAN_USBBUS1 as PcanChannel,
28 "2" => bind::PCAN_USBBUS2 as PcanChannel,
29 "3" => bind::PCAN_USBBUS3 as PcanChannel,
30 "4" => bind::PCAN_USBBUS4 as PcanChannel,
31 "5" => bind::PCAN_USBBUS5 as PcanChannel,
32 "6" => bind::PCAN_USBBUS6 as PcanChannel,
33 "7" => bind::PCAN_USBBUS7 as PcanChannel,
34 "8" => bind::PCAN_USBBUS8 as PcanChannel,
35 _ => return Err(AdapterError::UnknownDevice),
36 })
37}
38
39fn try_as_baud(baud: u32) -> Result<PcanBaud, AdapterError> {
40 Ok(match baud {
41 10_000 => bind::PCAN_BAUD_10K as PcanBaud,
42 20_000 => bind::PCAN_BAUD_20K as PcanBaud,
43 50_000 => bind::PCAN_BAUD_50K as PcanBaud,
44 100_000 => bind::PCAN_BAUD_100K as PcanBaud,
45 125_000 => bind::PCAN_BAUD_125K as PcanBaud,
46 250_000 => bind::PCAN_BAUD_250K as PcanBaud,
47 500_000 => bind::PCAN_BAUD_500K as PcanBaud,
48 800_000 => bind::PCAN_BAUD_800K as PcanBaud,
49 1_000_000 => bind::PCAN_BAUD_1M as PcanBaud,
50 _ => return Err(AdapterError::UnsupportedBaud),
51 })
52}
53
54impl Default for TPCANMsg {
55 fn default() -> Self {
56 Self {
57 ID: 0,
58 MSGTYPE: 0,
59 LEN: 0,
60 DATA: [0; 8],
61 }
62 }
63}
64
65impl Default for TPCANTimestamp {
66 fn default() -> Self {
67 Self {
68 millis: 0,
69 millis_overflow: 0,
70 micros: 0,
71 }
72 }
73}
74
75impl PcanAdapter {
76 pub fn new(
80 name: &str,
81 baud: AdapterBaud,
82 ) -> Result<Self, Box<dyn std::error::Error>> {
83 #[cfg(target_os = "macos")]
84 let lib = unsafe { libPCBUSB::new("libPCBUSB.dylib") }?;
85 #[cfg(target_os = "windows")]
86 let lib = unsafe { libPCBUSB::new("PCANBasic.dll") }?;
87
88 let channel = try_as_channel(name)?;
89
90 let status = unsafe {
91 lib.CAN_Initialize(
92 channel,
93 try_as_baud(baud)?,
94 bind::PCAN_USB as u8,
95 0,
96 0,
97 )
98 };
99 match status {
100 0 => {
101 let mut fd: c_int = 0;
102 let info_ptr: *mut c_int = &mut fd;
103 match unsafe {
104 lib.CAN_GetValue(
105 channel,
106 bind::PCAN_RECEIVE_EVENT as u8,
107 info_ptr as *mut ::std::os::raw::c_void,
108 4,
109 )
110 } {
111 bind::PCAN_ERROR_OK => Ok(Self { lib, fd, channel }),
112 _ => Err(Box::new(AdapterError::OpenFailed)),
113 }
114 }
115 _ => Err(Box::new(AdapterError::OpenFailed)),
116 }
117 }
118}
119
120impl Drop for PcanAdapter {
121 fn drop(&mut self) {
122 unsafe { self.lib.CAN_Uninitialize(self.channel.into()) };
123 }
124}
125
126impl Adapter for PcanAdapter {
127 fn send(&self, frame: &CanFrame) -> Result<(), Box<AdapterError>> {
128 let mut data: [u8; 8] = [0u8; 8];
129 data[..frame.dlc()].copy_from_slice(frame.data());
130
131 let mut msg = bind::TPCANMsg {
132 ID: id::raw_u32(frame.id()),
133 MSGTYPE: if frame.is_extended() {
134 bind::PCAN_MODE_EXTENDED
135 } else {
136 bind::PCAN_MODE_STANDARD
137 } as u8,
138 LEN: frame.dlc() as u8,
139 DATA: data,
140 };
141
142 let status = unsafe {
143 self.lib
144 .CAN_Write(self.channel.into(), &mut msg as *mut bind::TPCANMsg)
145 };
146 match status {
147 0 => Ok(()),
148 _ => Err(Box::new(AdapterError::WriteFailed)),
149 }
150 }
151
152 #[cfg(not(target_os = "windows"))]
153 fn recv(
154 &self,
155 timeout: Option<Duration>,
156 ) -> Result<CanFrame, Box<AdapterError>> {
157 use nix::poll::{PollFd, PollFlags, PollTimeout, poll};
158 use std::os::unix::io::BorrowedFd;
159
160 let start_time = Instant::now();
161
162 let mut msg = bind::TPCANMsg::default();
163 let mut timestamp = TPCANTimestamp::default();
164
165 loop {
166 let status = unsafe {
167 self.lib.CAN_Read(
168 self.channel.into(),
169 &mut msg as *mut bind::TPCANMsg,
170 &mut timestamp as *mut bind::TPCANTimestamp,
171 )
172 };
173
174 match status {
175 bind::PCAN_ERROR_OK => {
176 let id = match msg.MSGTYPE as u32 {
177 bind::PCAN_MODE_STANDARD => {
178 id::new_standard(msg.ID as u16)
179 }
180 bind::PCAN_MODE_EXTENDED => id::new_extended(msg.ID),
181 _ => return Err(Box::new(AdapterError::ReadFailed)),
182 }
183 .ok_or(Box::new(AdapterError::ReadFailed))?;
184
185 let frame =
186 CanFrame::new(id, &msg.DATA[..msg.LEN as usize])
187 .ok_or(Box::new(AdapterError::ReadFailed))?;
188 return Ok(frame);
189 }
190
191 bind::PCAN_ERROR_QRCVEMPTY => {
197 let poll_interval = match timeout {
198 Some(t) => {
199 let now = Instant::now();
200 let end = start_time + t;
201 if now >= end {
202 return Err(Box::new(
203 AdapterError::ReadTimeout,
204 ));
205 }
206
207 let remaining = (end - now).as_millis();
208
209 if remaining < 1000 {
210 PollTimeout::from(remaining as u16)
211 } else {
212 PollTimeout::from(1000u16)
213 }
214 }
215 None => PollTimeout::from(1000u16),
216 };
217
218 let pollfd = PollFd::new(
219 unsafe { BorrowedFd::borrow_raw(self.fd) },
220 PollFlags::POLLIN,
221 );
222 if poll(&mut [pollfd], poll_interval).is_err() {
223 return Err(Box::new(AdapterError::ReadFailed));
224 }
225 }
226
227 _ => return Err(Box::new(AdapterError::ReadFailed)),
228 }
229 }
230 }
231
232 #[cfg(target_os = "windows")]
233 fn recv(
234 &self,
235 timeout: Option<Duration>,
236 ) -> Result<CanFrame, Box<AdapterError>> {
237 let start_time = Instant::now();
238
239 let mut msg = bind::TPCANMsg::default();
240 let mut timestamp = TPCANTimestamp::default();
241
242 loop {
243 let status = unsafe {
244 self.lib.CAN_Read(
245 self.channel.into(),
246 &mut msg as *mut bind::TPCANMsg,
247 &mut timestamp as *mut bind::TPCANTimestamp,
248 )
249 };
250
251 match status {
252 bind::PCAN_ERROR_OK => {
253 let id = match msg.MSGTYPE as u32 {
254 bind::PCAN_MODE_STANDARD => {
255 id::new_standard(msg.ID as u16)
256 }
257 bind::PCAN_MODE_EXTENDED => id::new_extended(msg.ID),
258 _ => return Err(Box::new(AdapterError::ReadFailed)),
259 }
260 .ok_or(Box::new(AdapterError::ReadFailed))?;
261
262 let frame =
263 CanFrame::new(id, &msg.DATA[..msg.LEN as usize])
264 .ok_or(Box::new(AdapterError::ReadFailed))?;
265 return Ok(frame);
266 }
267
268 bind::PCAN_ERROR_QRCVEMPTY => {
271 if let Some(timeout) = timeout {
272 std::thread::sleep(Duration::from_micros(10));
273 if Instant::now() - start_time >= timeout {
274 return Err(Box::new(AdapterError::ReadTimeout));
275 }
276 }
277 }
278
279 _ => return Err(Box::new(AdapterError::ReadFailed)),
280 }
281 }
282 }
283}