1use log::{debug, error, trace, warn};
4use serialport::{SerialPortInfo, SerialPortType};
5use std::time::Duration;
6use thiserror::Error;
7use tokio::{
8 io::{AsyncReadExt, AsyncWriteExt, BufReader},
9 select,
10 time::sleep,
11};
12use tokio_serial::SerialStream;
13use vex_cdc::{
14 Decode, DecodeError, DecodeErrorKind, Encode, FixedString, FixedStringSizeError, REPLY_HEADER,
15 VarU16,
16 cdc2::{
17 Cdc2Ack,
18 controller::{UserDataPacket, UserDataPayload, UserDataReplyPacket},
19 },
20};
21
22use crate::{CheckHeader, Connection, ConnectionType, RawPacket, trim_packets};
23
24pub const VEX_USB_VID: u16 = 0x2888;
26
27pub const V5_BRAIN_USB_PID: u16 = 0x0501;
29
30pub const EXP_BRAIN_USB_PID: u16 = 0x600;
32
33pub const V5_CONTROLLER_USB_PID: u16 = 0x0503;
35
36pub const V5_SERIAL_BAUDRATE: u32 = 115200;
37
38#[derive(Clone, Debug)]
40pub struct VexSerialPort {
41 pub port_info: tokio_serial::SerialPortInfo,
42 pub port_type: VexSerialPortType,
43}
44
45#[derive(Debug, Clone, Copy, Eq, PartialEq)]
46pub enum VexSerialPortType {
47 User,
48 System,
49 Controller,
50}
51
52fn types_by_location(ports: &[SerialPortInfo]) -> Option<Vec<VexSerialPort>> {
55 debug!("Attempting to infer serial port types by port location.");
56 let mut vex_ports = Vec::new();
57
58 for port in ports {
59 let SerialPortType::UsbPort(info) = port.clone().port_type else {
62 continue;
63 };
64
65 if cfg!(target_os = "macos") && port.port_name.starts_with("/dev/tty.") {
66 debug!(
68 "Ignoring port named {:?} because it is a call-in device",
69 port.port_name
70 );
71 continue;
72 }
73
74 match info.pid {
75 V5_CONTROLLER_USB_PID => vex_ports.push(VexSerialPort {
76 port_info: port.clone(),
77 port_type: VexSerialPortType::Controller,
78 }),
79 V5_BRAIN_USB_PID | EXP_BRAIN_USB_PID => {
80 if let Some(mut location) = info.interface {
83 if cfg!(target_os = "macos") {
84 location -= 1; }
86
87 match location {
88 0 => {
89 debug!("Found a 'system' serial port over a Brain connection.");
90 vex_ports.push(VexSerialPort {
91 port_info: port.clone(),
92 port_type: VexSerialPortType::System,
93 })
94 }
95 1 => warn!(
96 "Found a controller serial port over a Brain connection! Things are most likely broken."
97 ),
98 2 => {
99 debug!("Found a 'user' serial port over a Brain connection.");
100 vex_ports.push(VexSerialPort {
101 port_info: port.clone(),
102 port_type: VexSerialPortType::User,
103 })
104 }
105 _ => warn!("Unknown location for V5 device: {}", location),
106 }
107 }
108 }
109 _ => {}
111 }
112 }
113
114 Some(vex_ports)
115}
116
117fn types_by_name_darwin(ports: &[SerialPortInfo]) -> Option<Vec<VexSerialPort>> {
121 assert!(cfg!(target_os = "macos"));
122
123 debug!("Attempting to infer serial port types by name. (Darwin fallback)");
124 let mut vex_ports = Vec::new();
125
126 for port in ports {
127 if cfg!(target_os = "macos") && port.port_name.starts_with("/dev/tty.") {
128 debug!(
130 "Ignoring port named {:?} because it is a call-in device",
131 port.port_name
132 );
133 continue;
134 }
135
136 let Some(interface) = port.port_name.chars().last() else {
137 continue;
138 };
139 match interface {
140 '1' => {
141 debug!("Found a 'system' serial port over a Brain connection.");
142 vex_ports.push(VexSerialPort {
143 port_info: port.clone(),
144 port_type: VexSerialPortType::System,
145 });
146 }
147 '2' => {
148 debug!("Found a controller serial port.");
149 vex_ports.push(VexSerialPort {
150 port_info: port.clone(),
151 port_type: VexSerialPortType::Controller,
152 });
153 }
154 '3' => {
155 debug!("Found a 'user' serial port over a Brain connection.");
156 vex_ports.push(VexSerialPort {
157 port_info: port.clone(),
158 port_type: VexSerialPortType::User,
159 });
160 }
161 _ => {
162 warn!("Unknown location for V5 device: {}", interface);
163 }
164 }
165 }
166
167 Some(vex_ports)
168}
169
170fn types_by_name_order(ports: &[SerialPortInfo]) -> Option<Vec<VexSerialPort>> {
175 debug!("Attempting to infer serial port types by order. (Windows fallback)");
176 if ports.len() != 2 {
177 return None;
178 }
179
180 let mut vex_ports = Vec::new();
181
182 let mut sorted_ports = ports.to_vec();
183 sorted_ports.sort_by_key(|info| info.port_name.clone());
185 sorted_ports.reverse();
186
187 vex_ports.push(VexSerialPort {
189 port_info: sorted_ports.pop().unwrap(),
190 port_type: VexSerialPortType::System,
191 });
192 vex_ports.push(VexSerialPort {
194 port_info: sorted_ports.pop().unwrap(),
195 port_type: VexSerialPortType::User,
196 });
197
198 if vex_ports.len() != ports.len() {
200 return None;
201 }
202
203 Some(vex_ports)
204}
205
206fn find_ports() -> Result<Vec<VexSerialPort>, SerialError> {
208 let ports = tokio_serial::available_ports()?;
210
211 let mut filtered_ports = Vec::new();
213
214 for port in ports {
216 let SerialPortType::UsbPort(port_info) = port.clone().port_type else {
219 continue;
220 };
221
222 if port_info.vid != VEX_USB_VID {
224 continue;
225 }
226
227 filtered_ports.push(port);
228 }
229
230 let vex_ports = types_by_location(&filtered_ports)
231 .or_else(|| {
232 if cfg!(target_os = "macos") {
233 types_by_name_darwin(&filtered_ports)
234 } else {
235 types_by_name_order(&filtered_ports)
236 }
237 })
238 .ok_or(SerialError::CouldntInferTypes)?;
239
240 Ok(vex_ports)
241}
242
243pub fn find_devices() -> Result<Vec<SerialDevice>, SerialError> {
245 let mut ports = find_ports()?.into_iter().peekable();
247
248 let mut devices = Vec::<SerialDevice>::new();
250
251 while let Some(port) = ports.next() {
253 match port.port_type {
255 VexSerialPortType::System => {
256 let port_name = port.port_info.port_name.clone();
257
258 if match ports.peek() {
260 Some(p) => p.port_type == VexSerialPortType::User,
261 _ => false,
262 } {
263 devices.push(SerialDevice::Brain {
264 system_port: port_name,
265 user_port: ports.next().unwrap().port_info.port_name.clone(),
266 });
267 } else {
268 devices.push(SerialDevice::Unknown {
270 system_port: port_name,
271 });
272 }
273 }
274 VexSerialPortType::User => {
275 if match ports.peek() {
277 Some(p) => p.port_type == VexSerialPortType::System,
278 _ => false,
279 } {
280 devices.push(SerialDevice::Brain {
281 system_port: ports.next().unwrap().port_info.port_name.clone(),
282 user_port: port.port_info.port_name.clone(),
283 });
284 }
285 }
286 VexSerialPortType::Controller => devices.push(SerialDevice::Controller {
287 system_port: port.port_info.port_name.clone(),
288 }),
289 }
290 }
291
292 Ok(devices)
294}
295
296#[derive(Clone, Debug)]
298pub enum SerialDevice {
299 Brain {
303 user_port: String,
304 system_port: String,
305 },
306
307 Controller { system_port: String },
311
312 Unknown { system_port: String },
319}
320
321impl SerialDevice {
322 pub fn connect(&self, timeout: Duration) -> Result<SerialConnection, SerialError> {
323 SerialConnection::open(self.clone(), timeout)
324 }
325
326 pub fn system_port(&self) -> String {
327 match &self {
328 Self::Brain {
329 system_port,
330 user_port: _,
331 }
332 | Self::Controller { system_port }
333 | Self::Unknown { system_port } => system_port.clone(),
334 }
335 }
336
337 pub fn user_port(&self) -> Option<String> {
338 match &self {
339 Self::Brain {
340 system_port: _,
341 user_port,
342 } => Some(user_port.clone()),
343 _ => None,
344 }
345 }
346}
347
348fn validate_header(mut data: &[u8]) -> Result<[u8; 2], DecodeError> {
350 let header = Decode::decode(&mut data)?;
351 if header != REPLY_HEADER {
352 return Err(DecodeError::new::<[u8; 2]>(DecodeErrorKind::InvalidHeader));
353 }
354 Ok(header)
355}
356
357#[derive(Debug)]
359pub struct SerialConnection {
360 system_port: SerialStream,
361 user_port: Option<BufReader<SerialStream>>,
362 incoming_packets: Vec<RawPacket>,
363}
364
365impl SerialConnection {
366 pub fn open(device: SerialDevice, timeout: Duration) -> Result<Self, SerialError> {
368 let system_port = match tokio_serial::SerialStream::open(
370 &tokio_serial::new(device.system_port(), 115200)
371 .parity(tokio_serial::Parity::None)
372 .timeout(timeout)
373 .stop_bits(tokio_serial::StopBits::One),
374 ) {
375 Ok(v) => Ok(v),
376 Err(e) => Err(SerialError::SerialportError(e)),
377 }?;
378
379 let user_port = if let Some(port) = &device.user_port() {
381 Some(match tokio_serial::SerialStream::open(
382 &tokio_serial::new(port, V5_SERIAL_BAUDRATE)
383 .parity(tokio_serial::Parity::None)
384 .timeout(timeout)
385 .stop_bits(tokio_serial::StopBits::One),
386 ) {
387 Ok(v) => Ok(BufReader::new(v)),
388 Err(e) => Err(SerialError::SerialportError(e)),
389 }?)
390 } else {
391 None
392 };
393
394 Ok(Self {
395 system_port,
396 user_port,
397 incoming_packets: Default::default(),
398 })
399 }
400
401 async fn receive_one_packet(&mut self) -> Result<(), SerialError> {
403 let mut header = [0u8; 2];
405 self.system_port.read_exact(&mut header).await?;
406
407 if let Err(e) = validate_header(&header) {
409 warn!(
410 "Skipping packet with invalid header: {:x?}. Error: {}",
411 header, e
412 );
413 return Ok(());
414 }
415
416 let mut packet = Vec::from(header);
418
419 packet.push(self.system_port.read_u8().await?);
421
422 let first_size_byte = self.system_port.read_u8().await?;
425 let size = if VarU16::check_wide(first_size_byte) {
426 let second_size_byte = self.system_port.read_u8().await?;
427 packet.extend([first_size_byte, second_size_byte]);
428
429 VarU16::decode(&mut [first_size_byte, second_size_byte].as_slice())?
431 } else {
432 packet.push(first_size_byte);
433
434 VarU16::decode(&mut [first_size_byte].as_slice())?
436 }
437 .into_inner() as usize;
438
439 let mut payload = vec![0; size];
441 self.system_port.read_exact(&mut payload).await?;
442
443 packet.extend(payload);
445
446 trace!("received packet: {:x?}", packet);
447
448 self.incoming_packets.push(RawPacket::new(packet));
450
451 Ok(())
452 }
453}
454
455impl Connection for SerialConnection {
456 type Error = SerialError;
457
458 fn connection_type(&self) -> ConnectionType {
459 if self.user_port.is_some() {
460 ConnectionType::Wired
461 } else {
462 ConnectionType::Controller
463 }
464 }
465
466 async fn send(&mut self, packet: impl Encode) -> Result<(), SerialError> {
467 let mut encoded = vec![0; packet.size()];
469 packet.encode(&mut encoded);
470
471 trace!("sent packet: {:x?}", encoded);
472
473 match self.system_port.write_all(&encoded).await {
475 Ok(_) => (),
476 Err(e) => return Err(SerialError::IoError(e)),
477 };
478
479 match self.system_port.flush().await {
480 Ok(_) => (),
481 Err(e) => return Err(SerialError::IoError(e)),
482 };
483
484 Ok(())
485 }
486
487 async fn recv<P: Decode + CheckHeader>(&mut self, timeout: Duration) -> Result<P, SerialError> {
488 select! {
490 result = async {
491 loop {
492 for packet in self.incoming_packets.iter_mut() {
493 if packet.check_header::<P>() {
494 match packet.decode_and_use::<P>() {
495 Ok(decoded) => {
496 trim_packets(&mut self.incoming_packets);
497 return Ok(decoded);
498 }
499 Err(e) => {
500 error!("Failed to decode packet with valid header: {}", e);
501 packet.used = true;
502 return Err(SerialError::DecodeError(e));
503 }
504 }
505 }
506 }
507 trim_packets(&mut self.incoming_packets);
508 self.receive_one_packet().await?;
509 }
510 } => result,
511 _ = sleep(timeout) => Err(SerialError::Timeout)
512 }
513 }
514
515 async fn read_user(&mut self, buf: &mut [u8]) -> Result<usize, SerialError> {
516 if let Some(user_port) = &mut self.user_port {
517 Ok(user_port.read(buf).await?)
518 } else {
519 let mut data = Vec::new();
520 loop {
521 let fifo = self
522 .handshake::<UserDataReplyPacket>(
523 Duration::from_millis(100),
524 1,
525 UserDataPacket::new(UserDataPayload {
526 channel: 1, write: None,
528 }),
529 )
530 .await?
531 .payload?;
532 if let Some(read) = fifo.data {
533 data.extend(read.as_bytes());
534 break;
535 }
536 }
537
538 let len = data.len().min(buf.len());
539 buf[..len].copy_from_slice(&data[..len]);
540
541 Ok(len)
542 }
543 }
544
545 async fn write_user(&mut self, mut buf: &[u8]) -> Result<usize, SerialError> {
546 if let Some(user_port) = &mut self.user_port {
547 Ok(user_port.write(buf).await?)
548 } else {
549 let buf_len = buf.len();
550 while !buf.is_empty() {
551 let (chunk, rest) = buf.split_at(std::cmp::min(224, buf.len()));
552 _ = self
553 .handshake::<UserDataReplyPacket>(
554 Duration::from_millis(100),
555 1,
556 UserDataPacket::new(UserDataPayload {
557 channel: 2, write: Some(
559 FixedString::new(String::from_utf8(chunk.to_vec()).unwrap())
560 .unwrap(),
561 ),
562 }),
563 )
564 .await?
565 .payload?;
566 buf = rest;
567 }
568
569 Ok(buf_len)
570 }
571 }
572}
573
574#[derive(Error, Debug)]
575pub enum SerialError {
576 #[error("IO Error: {0}")]
577 IoError(#[from] std::io::Error),
578
579 #[error("Packet decoding error: {0}")]
580 DecodeError(#[from] DecodeError),
581
582 #[error("Packet timeout")]
583 Timeout,
584
585 #[error("NACK received: {0:?}")]
586 Nack(#[from] Cdc2Ack),
587
588 #[error("Serialport Error")]
589 SerialportError(#[from] tokio_serial::Error),
590
591 #[error("Could not infer serial port types")]
592 CouldntInferTypes,
593
594 #[error(transparent)]
595 FixedStringSizeError(#[from] FixedStringSizeError),
596}