132 lines
3.5 KiB
Rust
132 lines
3.5 KiB
Rust
use serialport::SerialPort;
|
|
use std::{
|
|
io::{BufRead, BufReader, Write},
|
|
time::Duration,
|
|
};
|
|
|
|
pub(crate) struct Port(Inner<Box<dyn SerialPort>>);
|
|
pub(crate) struct LinesReader(Inner<BufReader<Box<dyn SerialPort>>>);
|
|
pub(crate) struct BytesReader(Inner<Box<dyn SerialPort>>);
|
|
|
|
struct Inner<I>(Option<I>);
|
|
|
|
impl<I> Inner<I> {
|
|
async fn with_inner<F, T, E>(&mut self, f: F) -> Result<T, anyhow::Error>
|
|
where
|
|
I: Send + 'static,
|
|
F: Fn(I) -> (I, Result<T, E>) + Send + 'static,
|
|
T: Send + 'static,
|
|
E: Send + 'static,
|
|
anyhow::Error: From<E>,
|
|
{
|
|
let item = self
|
|
.0
|
|
.take()
|
|
.ok_or_else(|| anyhow::anyhow!("Item has been dropped"))?;
|
|
|
|
let (item, res) = tokio::task::spawn_blocking(move || (f)(item)).await?;
|
|
|
|
self.0 = Some(item);
|
|
res.map_err(From::from)
|
|
}
|
|
|
|
fn as_ref(&self) -> Option<&I> {
|
|
self.0.as_ref()
|
|
}
|
|
|
|
fn take(&mut self) -> Option<I> {
|
|
self.0.take()
|
|
}
|
|
}
|
|
|
|
impl Port {
|
|
pub(crate) async fn open(name: String) -> Result<Self, anyhow::Error> {
|
|
tokio::task::spawn_blocking(move || {
|
|
serialport::new(name, 9600)
|
|
.timeout(Duration::from_secs(1))
|
|
.open()
|
|
.map_err(From::from)
|
|
})
|
|
.await?
|
|
.map(Some)
|
|
.map(Inner)
|
|
.map(Port)
|
|
}
|
|
|
|
pub(crate) fn try_clone(&self) -> Result<Self, anyhow::Error> {
|
|
if let Some(port) = self.0.as_ref() {
|
|
Ok(Port(Inner(Some(port.try_clone()?))))
|
|
} else {
|
|
Err(anyhow::anyhow!("Port has been dropped"))
|
|
}
|
|
}
|
|
|
|
pub(crate) async fn write(&mut self, bytes: Vec<u8>) -> Result<(), anyhow::Error> {
|
|
self.0
|
|
.with_inner(move |mut port| {
|
|
let res = port.write_all(&bytes);
|
|
|
|
(port, res)
|
|
})
|
|
.await
|
|
}
|
|
|
|
pub(crate) fn lines(mut self) -> LinesReader {
|
|
LinesReader(Inner(self.0.take().map(BufReader::new)))
|
|
}
|
|
|
|
pub(crate) fn bytes(mut self) -> BytesReader {
|
|
BytesReader(Inner(self.0.take()))
|
|
}
|
|
|
|
pub(crate) async fn bytes_to_write(&mut self) -> Result<u32, anyhow::Error> {
|
|
self.0
|
|
.with_inner(move |port| {
|
|
let res = port.bytes_to_write();
|
|
|
|
(port, res)
|
|
})
|
|
.await
|
|
}
|
|
}
|
|
|
|
impl BytesReader {
|
|
pub(crate) async fn read_byte(&mut self) -> Result<u8, anyhow::Error> {
|
|
self.0
|
|
.with_inner(move |mut port| {
|
|
let mut bytes = [0u8; 1];
|
|
let res = port.read_exact(&mut bytes);
|
|
(port, res.map(|_| bytes[0]))
|
|
})
|
|
.await
|
|
}
|
|
}
|
|
|
|
impl LinesReader {
|
|
pub(crate) async fn read_line(&mut self) -> Result<String, anyhow::Error> {
|
|
self.0
|
|
.with_inner(move |mut reader| {
|
|
let mut s = String::new();
|
|
let res = reader.read_line(&mut s);
|
|
(reader, res.map(|_| s))
|
|
})
|
|
.await
|
|
}
|
|
}
|
|
|
|
impl std::fmt::Debug for Port {
|
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
if let Some(name) = self.0.as_ref().and_then(|p| p.name()) {
|
|
f.debug_struct("Port").field("name", &name).finish()
|
|
} else {
|
|
f.debug_struct("Port").finish()
|
|
}
|
|
}
|
|
}
|
|
|
|
impl std::fmt::Debug for LinesReader {
|
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
f.debug_struct("LinesReader").finish()
|
|
}
|
|
}
|