#![warn(missing_docs)]
#![allow(rustdoc::broken_intra_doc_links)]
use serialport::SerialPort;
use std::io::{self, Write};
use std::thread;
use std::time::Duration;
pub struct Focus {
port: Box<dyn SerialPort>,
chunk_size: usize,
interval: u64,
progress_report: Box<dyn Fn(usize) + 'static>,
}
impl Focus {
pub fn create(device: &str) -> FocusBuilder {
FocusBuilder {
device,
chunk_size: 32,
interval: 50,
}
}
pub fn request(
&mut self,
command: &str,
args: Option<&[String]>,
) -> Result<String, std::io::Error> {
self.send(command, args)?.receive()
}
fn send(
&mut self,
command: &str,
args: Option<&[String]>,
) -> Result<&mut Self, std::io::Error> {
let request = format!("{} {}\n", command, args.unwrap_or_default().join(" "));
self.port.write_data_terminal_ready(true)?;
if self.chunk_size > 0 {
for c in request.as_bytes().chunks(self.chunk_size) {
self.port.write_all(c)?;
thread::sleep(Duration::from_millis(self.interval));
(self.progress_report)(c.len());
}
} else {
self.port.write_all(request.as_bytes())?;
(self.progress_report)(request.len());
}
Ok(self)
}
fn receive(&mut self) -> Result<String, std::io::Error> {
let mut buffer = [0; 1024];
let mut reply = vec![];
self.port.read_data_set_ready()?;
self.wait_for_data()?;
loop {
match self.port.read(buffer.as_mut_slice()) {
Ok(0) => break,
Ok(t) => {
reply.extend(&buffer[..t]);
(self.progress_report)(t);
}
Err(ref e) if e.kind() == io::ErrorKind::TimedOut => {
break;
}
Err(e) => {
return Err(e);
}
}
thread::sleep(Duration::from_millis(self.interval));
}
Ok(String::from_utf8_lossy(&reply)
.lines()
.filter(|l| !l.is_empty() && *l != ".")
.collect::<Vec<&str>>()
.join("\n"))
}
pub fn command(&mut self, command: &str) -> Result<String, std::io::Error> {
self.request(command, None)
}
pub fn set_progress_report(&mut self, progress_report: impl Fn(usize) + 'static) {
self.progress_report = Box::new(progress_report);
}
pub fn flush(&mut self) -> Result<&mut Self, std::io::Error> {
self.command(" ")?;
Ok(self)
}
pub fn find_devices() -> Option<Vec<String>> {
#[derive(PartialEq)]
struct DeviceDescriptor {
vid: u16,
pid: u16,
}
impl From<&serialport::UsbPortInfo> for DeviceDescriptor {
fn from(port: &serialport::UsbPortInfo) -> Self {
Self {
vid: port.vid,
pid: port.pid,
}
}
}
let supported_keyboards = [
DeviceDescriptor {
vid: 0x3496,
pid: 0x0006,
},
DeviceDescriptor {
vid: 0x1209,
pid: 0x2303,
},
DeviceDescriptor {
vid: 0x1209,
pid: 0x2301,
},
];
let devices: Vec<String> = serialport::available_ports()
.ok()?
.iter()
.filter_map(|p| match &p.port_type {
serialport::SerialPortType::UsbPort(port_info) => supported_keyboards
.contains(&port_info.into())
.then(|| p.port_name.to_string()),
_ => None,
})
.collect();
if devices.is_empty() {
return None;
}
Some(devices)
}
fn wait_for_data(&mut self) -> Result<(), std::io::Error> {
while self.port.bytes_to_read()? == 0 {
thread::sleep(Duration::from_millis(self.interval));
}
Ok(())
}
}
pub struct FocusBuilder<'a> {
device: &'a str,
chunk_size: usize,
interval: u64,
}
impl FocusBuilder<'_> {
pub fn chunk_size(mut self, chunk_size: usize) -> Self {
self.chunk_size = chunk_size;
self
}
pub fn interval(mut self, interval: u64) -> Self {
self.interval = interval;
self
}
pub fn open(&self) -> Result<Focus, serialport::Error> {
let port = serialport::new(self.device, 115200)
.timeout(Duration::from_millis(self.interval))
.open()?;
Ok(Focus {
port,
chunk_size: self.chunk_size,
interval: self.interval,
progress_report: Box::new(|_| {}),
})
}
}