Wire live serial data into matrix renderer
This commit is contained in:
96
src/serial_core/serial.rs
Normal file
96
src/serial_core/serial.rs
Normal file
@@ -0,0 +1,96 @@
|
||||
use crate::serial_core::codec::Codec;
|
||||
use crate::serial_core::codecs::tactile_a::TactileACodec;
|
||||
use crate::serial_core::frame::TactileAFrame;
|
||||
use crate::serial_core::utils::elapsed_millis;
|
||||
use crossbeam_channel::{Receiver, Sender, TryRecvError};
|
||||
use std::io::{Read, Write};
|
||||
use std::time::{Duration, Instant};
|
||||
|
||||
const POLL_INTERVAL_MS: u64 = 10;
|
||||
|
||||
/// Runs the serial polling loop on the calling (background) thread.
|
||||
/// Sends decoded pressure matrix data (Vec<i32>) to the output channel.
|
||||
pub fn run_serial_loop(
|
||||
port: &mut dyn ReadWrite,
|
||||
rows: usize,
|
||||
cols: usize,
|
||||
cancel_rx: &Receiver<()>,
|
||||
sample_tx: &Sender<Vec<i32>>,
|
||||
) {
|
||||
let session_started_at = Instant::now();
|
||||
let mut codec = TactileACodec::new(cols, rows);
|
||||
let req_frame = TactileACodec::build_req_frame(cols, rows);
|
||||
let mut buffer = [0u8; 1024];
|
||||
let mut poll_interval = Duration::from_millis(POLL_INTERVAL_MS);
|
||||
|
||||
loop {
|
||||
// Check cancel
|
||||
if cancel_rx.try_recv().is_ok() {
|
||||
break;
|
||||
}
|
||||
|
||||
// Send poll request
|
||||
if let Ok(req_bytes) = codec.encode(&req_frame) {
|
||||
let _ = port.write_all(&req_bytes);
|
||||
}
|
||||
|
||||
// Read response with poll interval
|
||||
let deadline = Instant::now() + poll_interval;
|
||||
loop {
|
||||
if Instant::now() >= deadline {
|
||||
break;
|
||||
}
|
||||
|
||||
match port.read(&mut buffer) {
|
||||
Ok(n) if n > 0 => {
|
||||
if let Ok(frames) = codec.decode(&buffer[..n], session_started_at) {
|
||||
for frame in frames {
|
||||
if let TactileAFrame::Rep(rep) = frame {
|
||||
if let Ok(vals) = TactileACodec::parse_data_frame(&rep.payload) {
|
||||
let _ = sample_tx.try_send(vals);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(_) => {
|
||||
std::thread::sleep(Duration::from_millis(1));
|
||||
}
|
||||
Err(ref e) if e.kind() == std::io::ErrorKind::TimedOut => {
|
||||
continue;
|
||||
}
|
||||
Err(e) => {
|
||||
eprintln!("[serial] read error: {e}");
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Trait abstracting read+write for the serial port
|
||||
pub trait ReadWrite: Send {
|
||||
fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize>;
|
||||
fn write_all(&mut self, buf: &[u8]) -> std::io::Result<()>;
|
||||
}
|
||||
|
||||
/// Wrapper for serialport's Box<dyn SerialPort>
|
||||
pub struct SerialPortReadWrite {
|
||||
inner: Box<dyn serialport::SerialPort>,
|
||||
}
|
||||
|
||||
impl SerialPortReadWrite {
|
||||
pub fn new(port: Box<dyn serialport::SerialPort>) -> Self {
|
||||
Self { inner: port }
|
||||
}
|
||||
}
|
||||
|
||||
impl ReadWrite for SerialPortReadWrite {
|
||||
fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
|
||||
self.inner.read(buf)
|
||||
}
|
||||
|
||||
fn write_all(&mut self, buf: &[u8]) -> std::io::Result<()> {
|
||||
self.inner.write_all(buf)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user